You are on page 1of 462

Curs practic de Java

Cristian Frsinaru a

Cuprins
1 Introducere Java n 1.1 Ce este Java ? . . . . . . . . . . . . . . . . . . 1.1.1 Limbajul de programare Java . . . . . 1.1.2 Platforme de lucru Java . . . . . . . . 1.1.3 Java: un limbaj compilat i interpretat s 1.2 Primul program . . . . . . . . . . . . . . . . . 1.3 Structura lexical a limbajului Java . . . . . . a 1.3.1 Setul de caractere . . . . . . . . . . . . 1.3.2 Cuvinte cheie . . . . . . . . . . . . . . 1.3.3 Identicatori . . . . . . . . . . . . . . . 1.3.4 Literali . . . . . . . . . . . . . . . . . . 1.3.5 Separatori . . . . . . . . . . . . . . . . 1.3.6 Operatori . . . . . . . . . . . . . . . . 1.3.7 Comentarii . . . . . . . . . . . . . . . 1.4 Tipuri de date i variabile . . . . . . . . . . . s 1.4.1 Tipuri de date . . . . . . . . . . . . . . 1.4.2 Variabile . . . . . . . . . . . . . . . . . 1.5 Controlul executiei . . . . . . . . . . . . . . . 1.5.1 Instructiuni de decizie . . . . . . . . . 1.5.2 Instructiuni de salt . . . . . . . . . . . 1.5.3 Instructiuni pentru tratarea exceptiilor 1.5.4 Alte instructiuni . . . . . . . . . . . . 1.6 Vectori . . . . . . . . . . . . . . . . . . . . . . 1.6.1 Crearea unui vector . . . . . . . . . . . 1.6.2 Tablouri multidimensionale . . . . . . 1.6.3 Dimensiunea unui vector . . . . . . . . 1.6.4 Copierea vectorilor . . . . . . . . . . . 1 11 11 11 12 13 14 16 16 16 17 17 19 19 20 21 21 22 24 24 25 26 26 26 26 28 28 29

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

2 1.6.5 Sortarea vectorilor - clasa Arrays . . . . . . 1.6.6 Vectori cu dimensiune variabil i eterogeni as Siruri de caractere . . . . . . . . . . . . . . . . . . Folosirea argumentelor de la linia de comand . . . a 1.8.1 Transmiterea argumentelor . . . . . . . . . . 1.8.2 Primirea argumentelor . . . . . . . . . . . . 1.8.3 Argumente numerice . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 30 30 31 31 32 34 35 35 35 37 38 39 39 40 41 42 46 49 50 50 52 53 56 57 58 59 59 61 62 63 64 64 66 66 67 67

1.7 1.8

2 Obiecte i clase s 2.1 Ciclul de viat al unui obiect . . . . . . . . . . . . . a 2.1.1 Crearea obiectelor . . . . . . . . . . . . . . . 2.1.2 Folosirea obiectelor . . . . . . . . . . . . . . 2.1.3 Distrugerea obiectelor . . . . . . . . . . . . 2.2 Crearea claselor . . . . . . . . . . . . . . . . . . . . 2.2.1 Declararea claselor . . . . . . . . . . . . . . 2.2.2 Extinderea claselor . . . . . . . . . . . . . . 2.2.3 Corpul unei clase . . . . . . . . . . . . . . . 2.2.4 Constructorii unei clase . . . . . . . . . . . . 2.2.5 Declararea variabilelor . . . . . . . . . . . . 2.2.6 this i super . . . . . . . . . . . . . . . . . . s 2.3 Implementarea metodelor . . . . . . . . . . . . . . 2.3.1 Declararea metodelor . . . . . . . . . . . . . 2.3.2 Tipul returnat de o metod . . . . . . . . . a 2.3.3 Trimiterea parametrilor ctre o metod . . . a a 2.3.4 Metode cu numr variabil de argumente . . a 2.3.5 Supra arcarea i supradenirea metodelor nc s 2.4 Modicatori de acces . . . . . . . . . . . . . . . . . 2.5 Membri de instant i membri de clas . . . . . . . a s a 2.5.1 Variabile de instant i de clas . . . . . . . a s a 2.5.2 Metode de instant i de clas . . . . . . . . a s a 2.5.3 Utilitatea membrilor de clas . . . . . . . . a 2.5.4 Blocuri statice de initializare . . . . . . . . . 2.6 Clase imbricate . . . . . . . . . . . . . . . . . . . . 2.6.1 Denirea claselor imbricate . . . . . . . . . . 2.6.2 Clase interne . . . . . . . . . . . . . . . . . 2.6.3 Identicare claselor imbricate . . . . . . . . 2.6.4 Clase anonime . . . . . . . . . . . . . . . . . 2.7 Clase i metode abstracte . . . . . . . . . . . . . . s

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS 2.7.1 Declararea unei clase abstracte 2.7.2 Metode abstracte . . . . . . . . 2.8 Clasa Object . . . . . . . . . . . . . . 2.8.1 Orice clas are o superclas . . a a 2.8.2 Clasa Object . . . . . . . . . . 2.9 Conversii automate ntre tipuri . . . . 2.10 Tipul de date enumerare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3 68 68 71 71 71 74 75 77 77 78 82 85 85 87 89 90 91 92 95 95 95 96 97 98 99 99 100 101 103 105 107 108 109 110 110 111

3 Exceptii 3.1 Ce sunt exceptiile ? . . . . . . . . . . . . 3.2 Prinderea i tratarea exceptiilor . . . . s 3.3 Aruncarea exceptiilor . . . . . . . . . . 3.4 Avantajele tratrii exceptiilor . . . . . . a 3.4.1 Separarea codului pentru tratarea 3.4.2 Propagarea erorilor . . . . . . . . 3.4.3 Gruparea erorilor dup tipul lor . a 3.5 Ierarhia claselor ce descriu exceptii . . . 3.6 Exceptii la executie . . . . . . . . . . . . 3.7 Crearea propriilor exceptii . . . . . . . .

. . . . . . . . . . . . . . . . . . . . erorilor . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

4 Intrri i ieiri a s s 4.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . 4.1.1 Ce sunt uxurile? . . . . . . . . . . . . . . . . . 4.1.2 Clasicarea uxurilor . . . . . . . . . . . . . . . 4.1.3 Ierarhia claselor pentru lucrul cu uxuri . . . . 4.1.4 Metode comune uxurilor . . . . . . . . . . . . 4.2 Folosirea uxurilor . . . . . . . . . . . . . . . . . . . . 4.2.1 Fluxuri primitive . . . . . . . . . . . . . . . . . 4.2.2 Fluxuri de procesare . . . . . . . . . . . . . . . 4.2.3 Crearea unui ux . . . . . . . . . . . . . . . . . 4.2.4 Fluxuri pentru lucrul cu iere . . . . . . . . . . s 4.2.5 Citirea i scrierea cu buer . . . . . . . . . . . . s 4.2.6 Concatenarea uxurilor . . . . . . . . . . . . . . 4.2.7 Fluxuri pentru ltrarea datelor . . . . . . . . . 4.2.8 Clasele DataInputStream i DataOutputStream s 4.3 Intrri i ieiri formatate . . . . . . . . . . . . . . . . . a s s 4.3.1 Intrri formatate . . . . . . . . . . . . . . . . . a 4.3.2 Ieiri formatate . . . . . . . . . . . . . . . . . . s

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

4 4.4

CUPRINS Fluxuri standard de intrare i ieire . . . . . . . . . . . . . s s 4.4.1 Asarea informatiilor pe ecran . . . . . . . . . . . . 4.4.2 Citirea datelor de la tastatur . . . . . . . . . . . . a 4.4.3 Redirectarea uxurilor standard . . . . . . . . . . . 4.4.4 Analiza lexical pe uxuri (clasa StreamTokenizer) a Clasa RandomAccesFile (iere cu acces direct) . . . . . . s Clasa File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 112 112 113 115 117 119

4.5 4.6

5 Interfete 5.1 Introducere . . . . . . . . . . . . . . . . . . 5.1.1 Ce este o interfat ? . . . . . . . . . a 5.2 Folosirea interfetelor . . . . . . . . . . . . . 5.2.1 Denirea unei interfete . . . . . . . . 5.2.2 Implementarea unei interfete . . . . . 5.2.3 Exemplu: implementarea unei stive . 5.3 Interfete i clase abstracte . . . . . . . . . . s 5.4 Motenire multipl prin interfete . . . . . . s a 5.5 Utilitatea interfetelor . . . . . . . . . . . . . 5.5.1 Crearea grupurilor de constante . . . 5.5.2 Transmiterea metodelor ca parametri 5.6 Interfata FilenameFilter . . . . . . . . . . 5.6.1 Folosirea claselor anonime . . . . . . 5.7 Compararea obiectelor . . . . . . . . . . . . 5.7.1 Interfata Comparable . . . . . . . . . 5.7.2 Interfata Comparator . . . . . . . . . 5.8 Adaptori . . . . . . . . . . . . . . . . . . . . 6 Organizarea claselor 6.1 Pachete . . . . . . . . . . . . . . . . . . 6.1.1 Pachetele standard (J2SDK) . . . 6.1.2 Folosirea membrilor unui pachet . 6.1.3 Importul unei clase sau interfete . 6.1.4 Importul la cerere dintr-un pachet 6.1.5 Importul static . . . . . . . . . . 6.1.6 Crearea unui pachet . . . . . . . 6.1.7 Denumirea unui pachet . . . . . . 6.2 Organizarea ierelor . . . . . . . . . . . s 6.2.1 Organizarea ierelor surs . . . . s a

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

121 . 121 . 121 . 122 . 122 . 123 . 124 . 129 . 130 . 132 . 132 . 133 . 134 . 137 . 138 . 139 . 141 . 142 145 145 145 146 147 148 149 150 151 152 152

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

CUPRINS 6.2.2 Organizarea unitilor de compilare (.class) at 6.2.3 Necesitatea organizrii ierelor . . . . . . . . a s 6.2.4 Setarea cii de cutare (CLASSPATH) . . . . a a Arhive JAR . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Folosirea utilitarului jar . . . . . . . . . . . . 6.3.2 Executarea aplicatiilor arhivate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 154 155 156 157 158 159

6.3

7 Colectii 7.1 Introducere . . . . . . . . . . 7.2 Interfete ce descriu colectii . . 7.3 Implementri ale colectiilor . . a 7.4 Folosirea ecient a colectiilor a 7.5 Algoritmi polimorci . . . . . 7.6 Tipuri generice . . . . . . . . 7.7 Iteratori i enumerri . . . . . s a

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

161 . 161 . 162 . 166 . 168 . 170 . 171 . 172 177 177 179 180 180 181 183 183 184 187 188 193 194 196

8 Serializarea obiectelor 8.1 Folosirea serializrii . . . . . . . . . . . . . . . a 8.1.1 Serializarea tipurilor primitive . . . . . 8.1.2 Serializarea obiectelor . . . . . . . . . . 8.1.3 Clasa ObjectOutputStream . . . . . . 8.1.4 Clasa ObjectInputStream . . . . . . . 8.2 Obiecte serializabile . . . . . . . . . . . . . . . 8.2.1 Implementarea interfetei Serializable . 8.2.2 Controlul serializrii . . . . . . . . . . a 8.3 Personalizarea serializrii obiectelor . . . . . . a 8.3.1 Controlul versiunilor claselor . . . . . . 8.3.2 Securizarea datelor . . . . . . . . . . . 8.3.3 Implementarea interfetei Externalizable 8.4 Clonarea obiectelor . . . . . . . . . . . . . . . 9 Interfata grac cu utilizatorul a 9.1 Introducere . . . . . . . . . . . . . . . . . . . 9.2 Modelul AWT . . . . . . . . . . . . . . . . . . 9.2.1 Componentele AWT . . . . . . . . . . 9.2.2 Suprafete de aare (Clasa Container) s 9.3 Gestionarea pozitionrii . . . . . . . . . . . . a 9.3.1 Folosirea gestionarilor de pozitionare .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

199 . 199 . 200 . 202 . 204 . 206 . 207

6 9.3.2 Gestionarul FlowLayout . . . . . . . . . . . 9.3.3 Gestionarul BorderLayout . . . . . . . . . . 9.3.4 Gestionarul GridLayout . . . . . . . . . . . 9.3.5 Gestionarul CardLayout . . . . . . . . . . . 9.3.6 Gestionarul GridBagLayout . . . . . . . . . 9.3.7 Gruparea componentelor (Clasa Panel) . . . Tratarea evenimentelor . . . . . . . . . . . . . . . . 9.4.1 Exemplu de tratare a evenimentelor . . . . . 9.4.2 Tipuri de evenimente . . . . . . . . . . . . . 9.4.3 Folosirea adaptorilor i a claselor anonime . s Folosirea ferestrelor . . . . . . . . . . . . . . . . . . 9.5.1 Clasa Window . . . . . . . . . . . . . . . . . 9.5.2 Clasa Frame . . . . . . . . . . . . . . . . . . 9.5.3 Clasa Dialog . . . . . . . . . . . . . . . . . . 9.5.4 Clasa FileDialog . . . . . . . . . . . . . . . Folosirea meniurilor . . . . . . . . . . . . . . . . . . 9.6.1 Ierarhia claselor ce descriu meniuri . . . . . 9.6.2 Tratarea evenimentelor generate de meniuri 9.6.3 Meniuri de context (popup) . . . . . . . . . 9.6.4 Acceleratori (Clasa MenuShortcut) . . . . . Folosirea componentelor AWT . . . . . . . . . . . . 9.7.1 Clasa Label . . . . . . . . . . . . . . . . . . 9.7.2 Clasa Button . . . . . . . . . . . . . . . . . 9.7.3 Clasa Checkbox . . . . . . . . . . . . . . . . 9.7.4 Clasa CheckboxGroup . . . . . . . . . . . . 9.7.5 Clasa Choice . . . . . . . . . . . . . . . . . 9.7.6 Clasa List . . . . . . . . . . . . . . . . . . . 9.7.7 Clasa ScrollBar . . . . . . . . . . . . . . . . 9.7.8 Clasa ScrollPane . . . . . . . . . . . . . . . 9.7.9 Clasa TextField . . . . . . . . . . . . . . . . 9.7.10 Clasa TextArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 210 211 212 214 218 219 221 224 227 232 232 233 236 239 242 243 246 247 250 250 251 252 253 255 257 259 261 262 263 265

9.4

9.5

9.6

9.7

10 Desenarea 10.1 Conceptul de desenare . . . . . . . . . . . . 10.1.1 Metoda paint . . . . . . . . . . . . . 10.1.2 Suprafete de desenare - clasa Canvas 10.2 Contextul grac de desenare . . . . . . . . . 10.2.1 Proprietile contextului grac . . . . at

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

269 . 269 . 270 . 271 . 274 . 275

CUPRINS 10.2.2 Primitive grace . . . . . . . . . . Folosirea fonturilor . . . . . . . . . . . . . 10.3.1 Clasa Font . . . . . . . . . . . . . . 10.3.2 Clasa FontMetrics . . . . . . . . . . Folosirea culorilor . . . . . . . . . . . . . . Folosirea imaginilor . . . . . . . . . . . . . 10.5.1 Aarea imaginilor . . . . . . . . . s 10.5.2 Monitorizarea arcrii imaginilor nc a 10.5.3 Mecanismul de double-buering . 10.5.4 Salvarea desenelor format JPEG n 10.5.5 Crearea imaginilor memorie . . n Tiprirea . . . . . . . . . . . . . . . . . . . a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7 275 276 277 279 282 286 287 289 291 291 292 293 299 299 299 300 301 304 305 307 310 310 314 316 316 316 319 324 329 332 335 336 336 338 340

10.3

10.4 10.5

10.6

11 Swing 11.1 Introducere . . . . . . . . . . . . . . . . . 11.1.1 JFC . . . . . . . . . . . . . . . . . 11.1.2 Swing API . . . . . . . . . . . . . . 11.1.3 Asemnri i deosebiri cu AWT . . a a s 11.2 Folosirea ferestrelor . . . . . . . . . . . . . 11.2.1 Ferestre interne . . . . . . . . . . . 11.3 Clasa JComponent . . . . . . . . . . . . . 11.4 Arhitectura modelului Swing . . . . . . . . 11.5 Folosirea modelelor . . . . . . . . . . . . . 11.5.1 Tratarea evenimentelor . . . . . . . 11.6 Folosirea componentelor . . . . . . . . . . 11.6.1 Componente atomice . . . . . . . . 11.6.2 Componente pentru editare de text 11.6.3 Componente pentru selectarea unor 11.6.4 Tabele . . . . . . . . . . . . . . . . 11.6.5 Arbori . . . . . . . . . . . . . . . . 11.6.6 Containere . . . . . . . . . . . . . . 11.6.7 Dialoguri . . . . . . . . . . . . . . 11.7 Desenarea . . . . . . . . . . . . . . . . . . 11.7.1 Metode specice . . . . . . . . . . 11.7.2 Consideratii generale . . . . . . . . 11.8 Look and Feel . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

8 12 Fire de executie 12.1 Introducere . . . . . . . . . . . . . . . . . 12.2 Crearea unui r de executie . . . . . . . . 12.2.1 Extinderea clasei Thread . . . . . . 12.2.2 Implementarea interfetei Runnable 12.3 Ciclul de viat al unui r de executie . . . a 12.3.1 Terminarea unui r de executie . . 12.3.2 Fire de executie de tip daemon . 12.3.3 Stabilirea prioritilor de executie . at 12.3.4 Sincronizarea relor de executie . . 12.3.5 Scenariul productor / consumator a 12.3.6 Monitoare . . . . . . . . . . . . . . 12.3.7 Semafoare . . . . . . . . . . . . . . 12.3.8 Probleme legate de sincronizare . . 12.4 Gruparea relor de executie . . . . . . . . 12.5 Comunicarea prin uxuri de tip pipe . . 12.6 Clasele Timer i TimerTask . . . . . . . . s

CUPRINS 343 . 343 . 344 . 345 . 347 . 352 . 355 . 357 . 358 . 362 . 362 . 367 . 369 . 371 . 373 . 376 . 378 383 . 383 . 385 . 387 . 388 . 393 . 397 401 . 401 . 402 . 404 . 406 . 408 . 410 . 412 . 416 . 420 . 421 . 421

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

13 Programare retea n 13.1 Introducere . . . . . . . . . . . . . . . . . . 13.2 Lucrul cu URL-uri . . . . . . . . . . . . . . 13.3 Socket-uri . . . . . . . . . . . . . . . . . . . 13.4 Comunicarea prin conexiuni . . . . . . . . . 13.5 Comunicarea prin datagrame . . . . . . . . . 13.6 Trimiterea de mesaje ctre mai multi clienti a 14 Appleturi 14.1 Introducere . . . . . . . . . . . . . . . 14.2 Crearea unui applet simplu . . . . . . . 14.3 Ciclul de viat al unui applet . . . . . a 14.4 Interfata grac cu utilizatorul . . . . . a 14.5 Denirea i folosirea parametrilor . . . s 14.6 Tag-ul APPLET . . . . . . . . . . . . 14.7 Folosirea relor de executie appleturi n 14.8 Alte metode oferite de clasa Applet . . 14.9 Arhivarea appleturilor . . . . . . . . . 14.10Restrictii de securitate . . . . . . . . . 14.11Appleturi care sunt i aplicatii . . . . . s

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

CUPRINS 15 Lucrul cu baze de date 15.1 Introducere . . . . . . . . . . . . . . . . . . 15.1.1 Generaliti despre baze de date . . . at 15.1.2 JDBC . . . . . . . . . . . . . . . . . 15.2 Conectarea la o baz de date . . . . . . . . . a 15.2.1 Inregistrarea unui driver . . . . . . . 15.2.2 Specicarea unei baze de date . . . . 15.2.3 Tipuri de drivere . . . . . . . . . . . 15.2.4 Realizarea unei conexiuni . . . . . . 15.3 Efectuarea de secvente SQL . . . . . . . . . 15.3.1 Interfata Statement . . . . . . . . . . 15.3.2 Interfata PreparedStatement . . . . . 15.3.3 Interfata CallableStatement . . . . . 15.3.4 Obtinerea i prelucrarea rezultatelor s 15.3.5 Interfata ResultSet . . . . . . . . . . 15.3.6 Exemplu simplu . . . . . . . . . . . . 15.4 Lucrul cu meta-date . . . . . . . . . . . . . 15.4.1 Interfata DatabaseMetaData . . . . . 15.4.2 Interfata ResultSetMetaData . . . . 16 Lucrul dinamic cu clase 16.1 Incrcarea claselor memorie . . . . . . a n 16.2 Mecanismul reectrii . . . . . . . . . . a 16.2.1 Examinarea claselor i interfetelor s 16.2.2 Manipularea obiectelor . . . . . . 16.2.3 Lucrul dinamic cu vectori . . . .

9 423 . 423 . 423 . 424 . 425 . 426 . 427 . 428 . 430 . 431 . 432 . 434 . 437 . 438 . 438 . 440 . 442 . 442 . 443 445 . 445 . 452 . 453 . 456 . 460

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

10

CUPRINS

Capitolul 1 Introducere Java n


1.1 Ce este Java ?

Java este o tehnologie inovatoare lansat de compania Sun Microsystems a n 1995, care a avut un impact remarcabil asupra ntregii comuniti a dezat voltatorilor de software, impunndu-se prin caliti deosebite cum ar sima at plitate, robustete i nu ultimul rnd portabilitate. Denumit initial OAK, s n a a tehnologia Java este format dintr-un limbaj de programare de nivel a nalt pe baza cruia sunt construite o serie de platforme destinate implementrii de a a aplicatii pentru toate segmentele industriei software.

1.1.1

Limbajul de programare Java

Inainte de a prezenta detaliu aspectele tehnice ale limbajului Java, s amn a intim caracteristicile sale principale, care l-au transformat ntr-un interval de timp att de scurt a ntr-una din cele mai pupulare optiuni pentru dezvoltarea de aplicatii, indiferent de domeniu sau de complexitatea lor. Simplitate - elimin supra arcarea operatorilor, motenirea multipl a nc s a i toate facilitile ce pot provoca scrierea unui cod confuz. s at Uurint crearea de aplicatii complexe ce folosesc programarea s a n n retea, re de executie, interfat grac, baze de date, etc. a a Robustete - elimin sursele frecvente de erori ce apar programare a n prin renuntarea la pointeri, administrarea automat a memoriei i elim a s 11

12

CAPITOLUL 1. INTRODUCERE JAVA IN inarea pierderilor de memorie printr-o procedur de colectare a obiectelor a care nu mai sunt referite, ce ruleaz fundal (garbage collector). a n Complet orientat pe obiecte - elimin complet stilul de programare a procedural. Securitate - este un limbaj de programare foarte sigur, furniznd a mecanisme stricte de securitate a programelor concretizate prin: vericarea dinamic a codului pentru detectarea secventelor periculoase, a impunerea unor reguli stricte pentru rularea proceselor la distant, etc. a Neutralitate arhitectural - comportamentul unei aplicatii Java nu a depinde de arhitectura zic a mainii pe care ruleaz. a s a Portabililtate - Java este un limbaj independent de platforma de lucru, aceeai aplicatie rulnd fr nici o modicare i fr a necesita res a aa s aa compilarea ei pe sisteme de operare diferite cum ar Windows, Linux, Mac OS, Solaris, etc. lucru care aduce economii substantiale rmelor dezvoltatoare de aplicatii. Este compilat i interpretat, aceasta ind solutia ecient pentru s a obtinerea portabilitii. at Performant - dei mai lent dect limbajele de programare care genereaz a s a a executabile native pentru o anumit platform de lucru, compilatorul a a Java asigur o performant ridicat a codului de octeti, astfel at a a a nc viteza de lucru putin mai sczut nu va un impediment dezvoltarea a a n de aplicatii orict de complexe, inclusiv grac 3D, animatie, etc. a a Este modelat dup C i C++, trecerea de la C, C++ la Java a s fcndu-se foarte uor. a a s

1.1.2

Platforme de lucru Java

Limbajul de programare Java a fost folosit la dezvoltarea unor tehnologii dedicate rezolvrii unor probleme din cele mai diverse domenii. Aceste tehnologii a au fost grupate aa numitele platforme de lucru, ce reprezint seturi de n s a librrii scrise limbajul Java, precum i diverse programe utilitare, folosite a n s pentru dezvoltarea de aplicatii sau componente destinate unei anume cate gorii de utilizatori.

1.1. CE ESTE JAVA ?

13

J2SE (Standard Edition) Este platforma standard de lucru ce ofer suport pentru crearea de a aplicatii independente i appleturi. s De asemenea, aici este inclus i tehnologia Java Web Start ce furnizeaz as a o modalitate extrem de facil pentru lansarea i instalarea local a proa s a gramelor scrise Java direct de pe Web, oferind cea mai comod solutie n a pentru distributia i actualizarea aplicatiilor Java. s J2ME (Micro Edition) Folosind Java, programarea dispozitivelor mobile este extrem de simpl, a platforma de lucru J2ME oferind suportul necesar scrierii de programe dedicate acestui scop. J2EE (Enterprise Edition) Aceast platform ofer API-ul necesar dezvoltrii de aplicatii coma a a a plexe, formate din componente ce trebuie s ruleze sisteme eterogene, a n cu informatiile memorate baze de date distribuite, etc. n Tot aici gsim i suportul necesar pentru crearea de aplicatii i servicii a s s Web, bazate pe componente cum ar servleturi, pagini JSP, etc. Toate distributiile Java sunt oferite gratuit i pot descrcate de pe s a Internet de la adresa http://java.sun.com. In continuare, vom folosi termenul J2SDK pentru a ne referi la distributia standard J2SE 1.5 SDK (Tiger).

1.1.3

Java: un limbaj compilat i interpretat s

In functie de modul de executie a aplicatiilor, limbajele de programare se mpart dou categorii: n a Interpretate: instructiunile sunt citite linie cu linie de un program numit interpretor i traduse instructiuni main. Avantajul acess n s a tei solutii este simplitatea i faptul c ind interpretat direct sursa s a a programului obtinem portabilitatea. Dezavantajul evident este viteza de executie redus. Probabil cel mai cunoscute limbaj interpretat este a limbajul Basic. Compilate: codul surs al programelor este transformat de compia lator ntr-un cod ce poate executat direct de procesor, numit cod

14

CAPITOLUL 1. INTRODUCERE JAVA IN main. Avantajul este executia extrem de rapid, dezavantajul ind s a a lipsa portabilitii, codul compilat at ntr-un format de nivel sczut nu a poate rulat dect pe platforma de lucru pe care a fost compilat. a

Limbajul Java combin solutiile amintite mai sus, programele Java ind a att interpretate ct i compilate. Aadar vom avea la dispozitie un compia a s s lator responsabil cu transformarea surselor programului aa numitul cod n s de octeti, precum i un interpretor ce va executa respectivul cod de octeti. s Codul de octeti este diferit de codul main. Codul main este reprezen s a s a tat de o succesiune de instructiuni specice unui anumit procesor i unei an s umite platforme de lucru reprezentate format binar astfel at s poat n nc a a executate fr a mai necesita nici o prelucrare. aa Codurile de octeti sunt seturi de instructiuni care seamn cu codul scris a a limbaj de asamblare i sunt generate de compilator independent de mediul n s de lucru. In timp ce codul main este executat direct de ctre procesor i s a a s poate folosit numai pe platforma pe care a fost creat, codul de octeti este interpretat de mediul Java i de aceea poate rulat pe orice platform pe s a care este instalat mediul de executie Java. a Prin maina virtual Java (JVM) vom elege mediul de executie al s a nt aplicatiilor Java. Pentru ca un cod de octeti s poat executat pe un a a anumit calculator, pe acesta trebuie s e instalat o main virtual Java. a a s a a Acest lucru este realizat automat de ctre distributia J2SDK. a

1.2

Primul program

Crearea oricrei aplicatii Java presupune efectuarea urmtorilor pai: a a s

1. Scriererea codului surs a

class FirstApp { public static void main( String args[]) { System.out.println("Hello world!"); } }

1.2. PRIMUL PROGRAM

15

Toate aplicatiile Java contin o clas principal(primar) care trebuie a a a n s se gaseasc metoda main. Clasele aplicatiei se pot gasi e a a ntr-un singur ier, e mai multe. s n

2. Salvarea ierelor surs s a Se va face iere care au obligatoriu extensia java, nici o alt extenn s a sie neind acceptat. Este recomandat ca ierul care contine codul surs a s a al clasei primare s aib acelai nume cu cel al clasei, dei acest lucru nu a a s s este obligatoriu. S presupunem c am salvat exemplul de mai sus ierul a a n s C:\intro\FirstApp.java.

3. Compilarea aplicatiei Pentru compilare vom folosi compilatorul javac din distributia J2SDK. Apelul compilatorului se face pentru ierul ce contine clasa principal a s a aplicatiei sau pentru orice ier/iere cu extensia java. Compilatorul creeaz s s a cte un ier separat pentru ecare clas a programului. Acestea au extensia a s a .class i implicit sunt plasate acelai director cu ierele surs. s n s s a javac FirstApp.java In cazul care compilarea a reuit va generat ierul FirstApp.class. n s s

4. Rularea aplicatiei Se face cu interpretorul java, apelat pentru unitatea de compilare corespunztoare clasei principale. Deoarece interpretorul are ca argument de a intrare numele clasei principale i nu numele unui ier, ne vom pozitiona s s directorul ce contine ierul FirstApp.class i vom apela interpretorul n s s astfel: java FirstApp Rularea unei aplicatii care nu folosete interfat grac, se va face s a a ntr-o fereastr sistem. a

16

CAPITOLUL 1. INTRODUCERE JAVA IN

Atentie Un apel de genul java c:\intro\FirstApp.class este greit! s

1.3
1.3.1

Structura lexical a limbajului Java a


Setul de caractere

Limbajului Java lucreaz mod nativ folosind setul de caractere Unicode. a n Acesta este un standard international care nlocuiete vechiul set de caractere s ASCII i care folosete pentru reprezentarea caracterelor 2 octeti, ceea ce s s nseamn c se pot reprezenta 65536 de semne, spre deosebire de ASCII, unde a a era posibil reprezentarea a doar 256 de caractere. Primele 256 caractere a Unicode corespund celor ASCII, referirea la celelalte fcndu-se prin \uxxxx, a a unde xxxx reprezint codul caracterului. a O alt caracteristic a setului de caractere Unicode este faptul c a a a ntreg intervalul de reprezentare a simbolurilor este divizat subintervale numite n blocuri, cteva exemple de blocuri ind: Basic Latin, Greek, Arabic, Gothic, a Currency, Mathematical, Arrows, Musical, etc. Mai jos sunt oferite cteva exemple de caractere Unicode. a \u0030 - \u0039 : cifre ISO-Latin 0 - 9 \u0660 - \u0669 : cifre arabic-indic 0 - 9 \u03B1 - \u03C9 : simboluri greceti s \u2200 - \u22FF : simboluri matematice (, , , etc.) \u4e00 - \u9fff : litere din alfabetul Han (Chinez, Japonez, Coreean) Mai multe informatii legate de reprezentarea Unicode pot obtinute la adresa http://www.unicode.org.

1.3.2

Cuvinte cheie

Cuvintele rezervate Java sunt, cu cteva exceptii, cele din C++ i au fost n a s enumerate tabelul de mai jos. Acestea nu pot folosite ca nume de clase, n

1.3. STRUCTURA LEXICALA A LIMBAJULUI JAVA

17

interfete, variabile sau metode. true, false, null nu sunt cuvinte cheie, dar nu pot nici ele folosite ca nume aplicatii. Cuvintele marcate prin n sunt rezervate, dar nu sunt folosite. abstract boolean break byte case catch char class const* continue default do double else extends final finally float for goto* if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while

Incepnd cu versiunea 1.5, mai exist i cuvntul cheie enum. a as a

1.3.3

Identicatori

Sunt secvente nelimitate de litere i cifre Unicode, s ncepnd cu o liter. Dup a a a cum am mai spus, identicatorii nu au voie s e identici cu cuvintele rezera vate.

1.3.4

Literali

Literalii pot de urmtoarele tipuri: a Intregi Sunt acceptate 3 baze de numeratie : baza 10, baza 16 ( ncep cu caracterele 0x) i baza 8 ( s ncep cu cifra 0) i pot de dou tipuri: s a normali - se reprezint pe 4 octeti (32 biti) a lungi - se reprezint pe 8 octeti (64 biti) i se termin cu caracterul a s a L (sau l).

18

CAPITOLUL 1. INTRODUCERE JAVA IN Flotanti Pentru ca un literal s e considerat otant el trebuie s aib cel putin o a a a zecimal dup virgul, s e notatie exponential sau s aib suxul a a a a n a a a F sau f pentru valorile normale - reprezentate pe 32 biti, respectiv D sau d pentru valorile duble - reprezentate pe 64 biti. Exemple: 1.0, 2e2, 3f, 4D. Logici Sunt reprezentati de true - valoarea logic de adevr, respectiv false a a - valoarea logic de fals. a

Atentie Spre deosebire de C++, literalii ntregi 1 i 0 nu mai au semnicatia s de adevrat, respectiv fals. a

Caracter Un literal de tip caracter este utilizat pentru a exprima caracterele codului Unicode. Reprezentarea se face e folosind o liter, e o secvent a a escape scris a ntre apostrofuri. Secventele escape permit specicarea caracterelor care nu au reprezentare grac i reprezentarea unor caras actere speciale precum backslash, apostrof, etc. Secventele escape pre denite Java sunt: n \b : Backspace (BS) \t : Tab orizontal (HT) \n : Linie nou (LF) a \f : Pagin nou (FF) a a \r : Inceput de rnd (CR) a \" : Ghilimele \ : Apostrof \\ : Backslash

1.3. STRUCTURA LEXICALA A LIMBAJULUI JAVA

19

Siruri de caractere Un literal ir de caractere este format din zero sau mai multe caractere s ntre ghilimele. Caracterele care formeaz irul pot caractere grace as sau secvente escape. Dac irul este prea lung el poate scris ca o concatenare de subiruri as s de dimensiune mai mic, concatenarea irurilor realizndu-se cu opera s a atorul +, ca exemplul: "Ana " + " are " + " mere ". Sirul vid n este "". Dup cum vom vedea, orice ir este de fapt o instant a clasei String, a s a denit pachetul java.lang. a n

1.3.5

Separatori

Un separator este un caracter care indic sfritul unei uniti lexicale i a as at s nceputul alteia. In Java separatorii sunt urmtorii: ( ) a [ ] ; , . . Instructiunile unui program se separ cu punct i virgul. a s a

1.3.6

Operatori

Operatorii Java sunt, cu mici deosebiri, cei din C++: atribuirea: = operatori matematici: +, -, *, /, %, ++, -- . Este permis notatia prescurtat de forma lval op= rval: x += 2 n a a -= 3 Exist operatori pentru autoincrementare i autodecrementare (post i a s s pre): x++, ++x, n--, --n Evaluarea expresiilor logice se face prin metoda scurtcircuitului: evaluarea se oprete momentul care valoarea de adevr a expresiei este s n n a sigur determinat. a operatori logici: &&(and), ||(or), !(not) operatori relationali: <, <=, >, <=, ==, != operatori pe biti: &(and), |(or), ^ (xor), ~ (not) operatori de translatie: <<, >>, >>> (shift la dreapta fr semn) a a

20

CAPITOLUL 1. INTRODUCERE JAVA IN operatorul if-else: expresie-logica ? val-true : val-false

operatorul , (virgul) folosit pentru evaluarea secvential a operatiilor: a a int x=0, y=1, z=2; operatorul + pentru concatenarea irurilor: s String s1="Ana"; String s2="mere"; int x=10; System.out.println(s1 + " are " + x + " " + s2); operatori pentru conversii (cast) : (tip-de-data) int a = (int)a; char c = (char)96; int i = 200; long l = (long)i; //widening conversion long l2 = (long)200; int i2 = (int)l2; //narrowing conversion

1.3.7

Comentarii

In Java exist trei feluri de comentarii: a Comentarii pe mai multe linii, nchise ntre /* i */. s Comentarii pe mai multe linii care in de documentatie, t nchise ntre /** i */. Textul dintre cele dou secvente este automat mutat s a n documentatia aplicatiei de ctre generatorul automat de documentatie a javadoc. Comentarii pe o singur linie, care incep cu //. a Observatii: Nu putem scrie comentarii interiorul altor comentarii. n Nu putem introduce comentarii interiorul literalilor caracter sau ir n s de caractere. Secventele /* i */ pot s apar pe o linie dup secventa // dar si s a a a pierd semnicatia. La fel se ntampl cu secventa // comentarii care a n incep cu /* sau */.

1.4. TIPURI DE DATE SI VARIABILE

21

1.4
1.4.1

Tipuri de date i variabile s


Tipuri de date

In Java tipurile de date se impart dou categorii: tipuri primitive i n a s tipuri referint. Java pornete de la premiza c orice este un obiect, a s a prin urmare tipurile de date ar trebui s e de fapt denite de clase i toate a s variabilele ar trebui s memoreze instante ale acestor clase (obiecte). In a principiu acest lucru este adevrat, a, pentru usurinta programrii, mai a ns a exist i aa numitele tipurile primitive de date, care sunt cele uzuale : as s aritmetice ntregi: byte (1 octet), short (2), int (4), long (8) reale: float (4 octeti), double (8) caracter: char (2 octeti) logic: boolean (true i false) s In alte limbaje de programare formatul i dimensiunea tipurilor primitive de s date pot depinde de platforma pe care ruleaz programul. In Java acest lucru a nu mai este valabil, orice dependent de o anumit platform specic ind a a a a eliminat. a

Vectorii, clasele i interfetele sunt tipuri referint. Valoarea unei variabile s a de acest tip este, spre deosebire de tipurile primitive, o referint (adres de a a memorie) ctre valoarea sau multimea de valori reprezentat de variabila a a respectiv. a

Exist trei tipuri de date din limbajul C care nu sunt suportate de lima bajul Java. Acestea sunt: pointer, struct i union. Pointerii au fost s eliminati din cauz c erau o surs constant de erori, locul lor ind luat de a a a a tipul referint, iar struct i union nu si mai au rostul att timp ct tipurile a s a a compuse de date sunt formate Java prin intermediul claselor. n

22

CAPITOLUL 1. INTRODUCERE JAVA IN

1.4.2

Variabile

Variabilele pot de tip primitiv sau referinte la obiecte (tip referint). In a diferent de tipul lor, pentru a putea folosite variabilele trebuie declarate i, s eventual, initializate. Declararea variabilelor: Tip numeVariabila; Initializarea variabilelor: Tip numeVariabila = valoare; Declararea constantelor: final Tip numeVariabila; Evident, exist posibilitatea de a declara i initializa mai multe variabile a s sau constante de acelai tip s ntr-o singur instructiune astfel: a Tip variabila1[=valoare1], variabila2[=valoare2],...; Conventia de numire a variabilelor Java include, printre altele, urmtoarele n a criterii: variabilele nale (constante) se scriu cu majuscule; variabilele care nu sunt constante se scriu astfel: prima liter mic iar a a dac numele variabilei este format din mai multi atomi lexicali, atunci a primele litere ale celorlalti atomi se scriu cu majuscule. Exemple: final double PI = 3.14; final int MINIM=0, MAXIM = 10; int valoare = 100; char c1=j, c2=a, c3=v, c4=a; long numarElemente = 12345678L; String bauturaMeaPreferata = "apa"; In functie de locul care sunt declarate variabilele se n mpart urmtoatele n a categorii: a. Variabile membre, declarate interiorul unei clase, vizibile pentru n toate metodele clasei respective ct i pentru alte clase functie de a s n nivelul lor de acces (vezi Declararea variabilelor membre).

1.4. TIPURI DE DATE SI VARIABILE b. Parametri metodelor, vizibili doar metoda respectiv. n a

23

c. Variabile locale, declarate ntr-o metod, vizibile doar metoda rea n spectiv. a d. Variabile locale, declarate ntr-un bloc de cod, vizibile doar blocul n respectiv. e. Parametrii de la tratarea exceptiilor (vezi Tratarea exceptiilor). class Exemplu { //Fiecare variabila corespunde situatiei data de numele ei //din enumerarea de mai sus int a; public void metoda(int b) { a = b; int c = 10; for(int d=0; d < 10; d++) { c --; } try { a = b/c; } catch(ArithmeticException e) { System.err.println(e.getMessage()); } } } Observatii: Variabilele declarate ntr-un for, rmn locale corpului ciclului: a a for(int i=0; i<100; i++) { //domeniul de vizibilitate al lui i } i = 101;//incorect Nu este permis ascunderea unei variabile: a

24

CAPITOLUL 1. INTRODUCERE JAVA IN int x=1; { int x=2; //incorect }

1.5

Controlul executiei

Instructiunile Java pentru controlul executiei sunt foarte asemntoare celor a a din limbajul C i pot artite urmtoarele categorii: s mp n a Instructiuni de decizie: if-else, switch-case Instructiuni de salt: for, while, do-while Instructiuni pentru tratarea exceptiilor: try-catch-finally, throw Alte instructiuni: break, continue, return, label:

1.5.1
if-else

Instructiuni de decizie

if (expresie-logica) { ... } if (expresie-logica) { ... } else { ... } switch-case switch (variabila) { case valoare1: ... break; case valoare2:

1.5. CONTROLUL EXECUTIEI ... break; ... default: ... }

25

Variabilele care pot testate folosind instructiunea switch nu pot dect a de tipuri primitive.

1.5.2
for

Instructiuni de salt

for(initializare; expresie-logica; pas-iteratie) { //Corpul buclei } for(int i=0, j=100 ; i < 100 && j > 0; i++, j--) { ... } Att la initializare ct i pasul de iteratie pot mai multe instructiuni a a s n desprtite prin virgul. a a

while while (expresie-logica) { ... }

do-while do { ... } while (expresie-logica);

26

CAPITOLUL 1. INTRODUCERE JAVA IN

1.5.3

Instructiuni pentru tratarea exceptiilor

Instructiunile pentru tratarea exceptiilor sunt try-catch-finally, respectiv throw i vor tratate capitolul Exceptii. s n

1.5.4

Alte instructiuni

break: prsete fortat corpul unei structuri repetitive. aa s continue: termina fortat iteratia curent a unui ciclu i trece la urmtoarea a s a iteratie. return [valoare]: termin o metod i, eventual, returneaz o valoa as a rare. numeEticheta: : Denete o etichet. s a Dei Java nu exist goto, se pot deni totui etichete folosite expresii s n a s n de genul: break numeEticheata sau continue numeEticheta, utile pentru a controla punctul de ieire dintr-o structur repetitiv, ca s a a nexemplul de mai jos: i=0; eticheta: while (i < 10) { System.out.println("i="+i); j=0; while (j < 10) { j++; if (j==5) continue eticheta; if (j==7) break eticheta; System.out.println("j="+j); } i++; }

1.6
1.6.1

Vectori
Crearea unui vector

Crearea unui vector presupune realizarea urmtoarelor etape: a

1.6. VECTORI

27

Declararea vectorului - Pentru a putea utiliza un vector trebuie, nainte de toate, sa-l declarm. Acest lucru se face prin expresii de forma: a Tip[] numeVector; sau Tip numeVector[]; ca exemplele de mai jos: n int[] intregi; String adrese[]; Instantierea Declararea unui vector nu implic i alocarea memoriei necesare pentru as retinerea elementelor. Operatiunea de alocare a memoriei, numit i a s instantierea vectorului, se realizeaz a ntotdeauna prin intermediul operatorului new. Instantierea unui vector se va face printr-o expresie de genul: numeVector = new Tip[nrElemente]; unde nrElemente reprezint numrul maxim de elemente pe care le a a poate avea vectorul. In urma instantierii vor alocati: nrElemente dimensiune(T ip) octeti necesari memorrii elementelor din vector, unde a prin dimensiune(T ip) am notat numrul de octeti pe care se reprezint a a tipul respectiv. v = new int[10]; //aloca spatiu pentru 10 intregi: 40 octeti c = new char[10]; //aloca spatiu pentru 10 caractere: 20 octeti Declararea i instantierea unui vector pot fcute simultan astfel: s a Tip[] numeVector = new Tip[nrElemente];

28

CAPITOLUL 1. INTRODUCERE JAVA IN Initializarea (optional) Dup declararea unui vector, acesta poate a initializat, adic elementele sale pot primi nite valori initiale, evident a s dac este cazul pentru aa ceva. In acest caz instantierea nu mai trebuie a s facut explicit, alocarea memoriei fcndu-se automat functie de a a a n num rul de elemente cu care se initializeaz vectorul. a a String culori[] = {"Rosu", "Galben", "Verde"}; int []factorial = {1, 1, 2, 6, 24, 120};

Primul indice al unui vector este 0, deci pozitiile unui vector cu n ele mente vor cuprinse ntre 0 i n 1. Nu sunt permise constructii de genul s Tip numeVector[nrElemente], alocarea memoriei fcndu-se doar prin ina a termediul opearatorului new. int v[10]; int v[] = new int[10]; //ilegal //corect

1.6.2

Tablouri multidimensionale

In Java tablourile multidimensionale sunt de fapt vectori de vectori. De exemplu, crearea i instantierea unei matrici vor realizate astfel: s Tip matrice[][] = new Tip[nrLinii][nrColoane]; matrice[i] este linia i a matricii i reprezint un vector cu nrColoane s a elemente iar matrice[i][j] este elementul de pe linia i i coloana j. s

1.6.3

Dimensiunea unui vector

Cu ajutorul variabilei length se poate aa numrul de elemente al unui a vector. int []a = new int[5]; // a.length are valoarea 5 int m[][] = new int[5][10]; // m[0].length are valoarea 10 Pentru a elege modalitatea de folosire a lui length trebuie mentionat c nt a ecare vector este de fapt o instant a unei clase iar length este o variabil a a public a acelei clase, care este retinut numrul maxim de elemente al a n a vectorului.

1.6. VECTORI

29

1.6.4

Copierea vectorilor

Copierea elementelor unui vector a ntr-un alt vector b se poate face, e element cu element, e cu ajutorul metodei System.arraycopy, ca exemn plele de mai jos. Dup cum vom vedea, o atribuire de genul b = a are alt a a semnicatie dect copierea elementelor lui a b i nu poate folosit a n s a n acest scop. int a[] = {1, 2, 3, 4}; int b[] = new int[4]; // Varianta 1 for(int i=0; i<a.length; i++) b[i] = a[i]; // Varianta 2 System.arraycopy(a, 0, b, 0, a.length); // Nu are efectul dorit b = a;

1.6.5

Sortarea vectorilor - clasa Arrays

In Java s-a pus un accent deosebit pe implementarea unor structuri de date i algoritmi care s simplice proceseul de crearea a unui algoritm, programs a atorul trebuind s se concentreze pe aspectele specice problemei abordate. a Clasa java.util.Arrays ofer diverse metode foarte utile lucrul cu veca n tori cum ar : sort - sorteaz ascendent un vector, folosind un algoritm de tip Quicka Sort performant, de complexitate O(n log(n)). int v[]={3, 1, 4, 2}; java.util.Arrays.sort(v); // Sorteaza vectorul v // Acesta va deveni {1, 2, 3, 4} binarySearch - cutarea binar a unei anumite valori a a ntr-un vector sortat;

30

CAPITOLUL 1. INTRODUCERE JAVA IN equals - testarea egalitii valorilor a doi vectori (au aceleai numr at s a de elemente i pentru ecare indice valorile corespunztoare din cei doi s a vectori sunt egale) ll - atribuie ecrui element din vector o valoare specicat. a a

1.6.6

Vectori cu dimensiune variabil i eterogeni as

Implementri ale vectorilor cu numr variabil de elemente sunt oferite de a a clase specializate cum ar Vector sau ArrayList din pachetul java.util. Astfel de obiecte descriu vectori eterogeni, ale cror elemente au tipul Object, a i vor studiati capitolul Colectii. s n

1.7

Siruri de caractere

In Java, un ir de caractere poate reprezentat printr-un vector format s din elemente de tip char, un obiect de tip String sau un obiect de tip StringBuer. Dac un ir de caractere este constant (nu se dorete schimbarea continutului a s s s pe parcursul executiei programului) atunci el va declarat de tipul String, a altfel va declarat de tip StringBuffer. Diferenta principal a ntre aceste clase este c StringBuffer pune la dispozitie metode pentru modicarea a continutului irului, cum ar : append, insert, delete, reverse. s Uzual, cea mai folosit modalitate de a lucra cu iruri este prin intermediul a s clasei String, care are i unele particulariti fat de restul claselor menite s s at a a simplice ct mai mult folosirea irurilor de caractere. Clasa StringBuffer a s va utilizat predominant aplicatii dedicate procesrii textelor cum ar a n a editoarele de texte. Exemple echivalente de declarare a unui ir: s String s = "abc"; String s = new String("abc"); char data[] = {a, b, c}; String s = new String(data); Observati prima variant de declarare a irului s din exemplul de mai sus a s - de altfel, cea mai folosit - care prezint o particularitate a clasei String a a fata de restul claselor Java referitoare la instantierea obiectelor sale.

1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA 31 Concatenarea irurilor de caractere se face prin intermediul operatorului s + sau, cazul irurilor de tip StringBuffer, folosind metoda append. n s String s1 = "abc" + "xyz"; String s2 = "123"; String s3 = s1 + s2; In Java, operatorul de concatenare + este extrem de exibil, sensul c n a permite concatenarea irurilor cu obiecte de orice tip care au o reprezentare s de tip ir de caractere. Mai jos, sunt cteva exemple: s a System.out.print("Vectorul v are" + v.length + " elemente"); String x = "a" + 1 + "b" Pentru a lmuri putin lucrurile, ceea ce execut compilatorul atunci cnd a a a alnete o secvent de genul String x = "a" + 1 + "b" este: nt s a String x = new StringBuffer().append("a").append(1). append("b").toString() Atentie a la ordinea de efectuare a operatiilor. Sirul s=1+2+"a"+1+2 ns va avea valoarea "3a12", primul + ind operatorul matematic de adunare iar al doilea +, cel de concatenare a irurilor. s

1.8
1.8.1

Folosirea argumentelor de la linia de comand a


Transmiterea argumentelor

O aplicatie Java poate primi oricte argumente de la linia de comanda a n momentul lansrii ei. Aceste argumente sunt utile pentru a permite utilizaa torului s specice diverse optiuni legate de functionarea aplicatiei sau s a a furnizeze anumite date initiale programului.

Atentie Programele care folosesc argumente de la linia de comand nu sunt 100% a pure Java, deoarece unele sisteme de operare, cum ar Mac OS, nu au n mod normal linie de comand. a

32

CAPITOLUL 1. INTRODUCERE JAVA IN

Argumentele de la linia de comand sunt introduse la lansarea unei aplicatii, a ind specicate dup numele aplicatiei i separate prin spatiu. De exemplu, a s s presupunem c aplicatia Sortare ordoneaz lexicograc (alfabetic) liniile a a a unui ier i primete ca argument de intrare numele ierului pe care s s s s s a sorteze. Pentru a ordona ierul "persoane.txt", aplicatia va lansat l s a astfel: java Sortare persoane.txt Aadar, formatul general pentru lansarea unei aplicatii care primete argus s mente de la linia de comand este: a java NumeAplicatie [arg0 arg1 . . . argn] In cazul care sunt mai multe, argumentele trebuie separate prin spatii n iar dac unul dintre argumente contine spatii, atunci el trebuie pus a ntre ghilimele. Evident, o aplicatie poate s nu primeasc nici un argument sau a a poate s ignore argumentele primite de la linia de comand. a a

1.8.2

Primirea argumentelor

In momentul lansrii unei aplicatii interpretorul parcurge linia de comand cu a a care a fost lansat aplicattia i, cazul care exist, transmite programului a s n n a argumentele specicate sub forma unui vector de iruri. Acesta este primit s de aplicatie ca parametru al metodei main. Reamintim c formatul metodei a main din clasa principal este: a public static void main (String args[]) Vectorul args primit ca parametru de metoda main va contine toate argu mentele transmise programului de la linia de comand. a In cazul apelului java Sortare persoane.txt vectorul args va contine un singur element pe prima s pozitie: args[0]="persoane.txt". a

Vectoru args este instantiat cu un numr de elemente egal cu numrul ar a a gumentelor primite de la linia de comand. Aadar, pentru a aa numrul de a s a argumente primite de program este sucient s am dimensiunea vectorului a a args prin intermediul atributului length:

1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA 33 public static void main (String args[]) { int numarArgumente = args.length ; } In cazul care aplicatia presupune existenta unor argumente de la linia n de comand, a acestea nu au fost transmise programului la lansarea sa, vor a ns aprea exceptii (erori) de tipul ArrayIndexOutOfBoundsException. Tratarea a acestor exceptii este prezentat capitolul Exceptii. a n Din acest motiv, este necesar s testm dac programul a primit argumentele a a a de la linia de comand necesare pentru functionarea sa i, caz contrar, s a s n a aeze un mesaj de avertizare sau s foloseasc nite valori implicite, ca s a a s n exemplul de mai jos: public class Salut { public static void main (String args[]) { if (args.length == 0) { System.out.println("Numar insuficient de argumente!"); System.exit(-1); //termina aplicatia } String nume = args[0]; //exista sigur String prenume; if (args.length >= 1) prenume = args[1]; else prenume = ""; //valoare implicita System.out.println("Salut " + nume + " " + prenume); } } Spre deosebire de limbajul C, vectorul primit de metoda main nu contine pe prima pozitie numele aplicatiei, ntruct Java numele aplicatiei este a n chiar numele clasei principale, adic a clasei care se gasete metoda main. a n s S consider continuare un exemplu simplu care se dorete aarea a a n n s s pe ecran a argumentelor primite de la linia de comand: a public class Afisare { public static void main (String[] args) { for (int i = 0; i < args.length; i++) System.out.println(args[i]);

34 } }

CAPITOLUL 1. INTRODUCERE JAVA IN

Un apel de genul java Afisare Hello Java va produce urmtorul rezula tat (aplicatia a primit 2 argumente): Hello Java Apelul java Afisare "Hello Java" va produce a alt rezultat (aplicatia ns a primit un singur argument): Hello Java

1.8.3

Argumente numerice

Argumentele de la linia de comand sunt primite sub forma unui vector de a iruri (obiecte de tip String). In cazul care unele dintre acestea reprezint s n a valori numerice ele vor trebui convertite din iruri numere. Acest lucru s n se realizeaz cu metode de tipul parseTipNumeric aate clasa corespuna n zatoare tipului care vrem s facem conversia: Integer, Float, Double, n a etc. S considerm, de exemplu, c aplicatia Power ridic un numar real la o a a a a putere ntreag, argumentele ind trimise de la linia de comand sub forma: a a java Power "1.5" "2" //ridica 1.5 la puterea 2 Conversia celor dou argumente numere se va face astfel: a n public class Power { public static void main(String args[]) { double numar = Double.parseDouble(args[0]); int putere = Integer.parseInt(args[1]); System.out.println("Rezultat=" + Math.pow(numar, putere)); } } Metodele de tipul parseTipNumeric pot produce exceptii (erori) de tipul NumberFormatException cazul care irul primit ca parametru nu reprezint n n s a un numar de tipul respectiv. Tratarea acestor exceptii este prezentat a n capitolul Exceptii.

Capitolul 2 Obiecte i clase s


2.1
2.1.1

Ciclul de viat al unui obiect a


Crearea obiectelor

In Java, ca orice limbaj de programare orientat-obiect, crearea obiectelor n se realizeaz prin instantierea unei clase i implic urmtoarele lucruri: a s a a Declararea Presupune specicarea tipului acelui obiect, cu alte cuvinte specicarea clasei acestuia (vom vedea c tipul unui obiect poate i o interfat). a s a NumeClasa numeObiect; Instantierea Se realizeaz prin intermediul operatorului new i are ca efect crearea a s efectiv a obiectului cu alocarea spatiului de memorie corespunztor. a a numeObiect = new NumeClasa(); Initializarea Se realizeaz prin intermediul constructorilor clasei respective. Initializarea a este de fapt parte integrant a procesului de instantiere, sensul c a n a imediat dup alocarea memoriei ca efect al operatorului new este apelat a constructorul specicat. Parantezele rotunde de dup numele clasei ina dic faptul c acolo este de fapt un apel la unul din constructorii clasei a a i nu simpla specicare a numelui clasei. s Mai general, instantierea i initializarea apar sub forma: s 35

36

CAPITOLUL 2. OBIECTE SI CLASE numeObiect = new NumeClasa([argumente constructor]);

S considerm urmtorul exemplu, care declarm i instantiem dou a a a n a s a obiecte din clasa Rectangle, clas ce descrie suprafete grace rectangulare, a denite de coordonatele coltului stnga sus (originea) i limea, respectiv a s at altimea. n Rectangle r1, r2; r1 = new Rectangle(); r2 = new Rectangle(0, 0, 100, 200); In primul caz Rectangle() este un apel ctre constructorul clasei Rectangle a care este responsabil cu initializarea obiectului cu valorile implicite. Dup a cum observm al doilea caz, initializarea se poate face i cu anumiti paraa n s metri, cu conditia s existe un constructor al clasei respective care s accepte a a parametrii respectivi. Fiecare clas are un set de constructori care se ocup cu initializare a a obiectelor nou create. De exemplu, clasa Rectangle are urmtorii construca tori: public public public public public public Rectangle() Rectangle(int latime, int inaltime) Rectangle(int x, int y, int latime, int inaltime) Rectangle(Point origine) Rectangle(Point origine, int latime, int inaltime) Rectangle(Point origine, Dimension dimensiune)

Declararea, instantierea i initializarea obiectului pot aprea pe aceeai s a s linie (cazul cel mai uzual): Rectangle patrat = new Rectangle(0, 0, 100, 100); Obiecte anonime Este posibil i crearea unor obiecte anonime care servesc doar pentru a s initializarea altor obiecte, caz care etapa de declarare a referintei obiectului n nu mai este prezent: a Rectangle patrat = new Rectangle(new Point(0,0), new Dimension(100, 100));

2.1. CICLUL DE VIATA AL UNUI OBIECT

37

Spatiul de memorie nu este pre-alocat Declararea unui obiect nu implic sub nici o form alocarea de spatiu a a de memorie pentru acel obiect. Alocarea memoriei se face doar la apelul operatorului new. Rectangle patrat; patrat.x = 10; //Eroare - lipseste instantierea

2.1.2

Folosirea obiectelor

Odat un obiect creat, el poate folosit urmtoarele sensuri: aarea unor a n a informatii despre obiect, schimbarea strii sale sau executarea unor actiuni. a Aceste lucruri se realizeaza prin aarea sau schimbarea valorilor variabilelor sale, respectiv prin apelarea metodelor sale. Referirea valorii unei variabile se face prin obiect.variabila De exemplu clasa Rectangle are variabilele publice x, y, width, height, origin. Aarea valorilor acestor variabile sau schimbarea lor se face prin constructii de genul: Rectangle patrat = new Rectangle(0, 0, 100, 200); System.out.println(patrat.width); //afiseaza 100 patrat.x = 10; patrat.y = 20; //schimba originea patrat.origin = new Point(10, 20); //schimba originea Accesul la variabilele unui obiect se face conformitate cu drepturile de n acces pe care le ofer variabilele respective celorlalte clase. (vezi Modicaa tori de acces pentru membrii unei clase) Apelul unei metode se face prin obiect.metoda([parametri]). Rectangle patrat = new Rectangle(0, 0, 100, 200); patrat.setLocation(10, 20); //schimba originea patrat.setSize(200, 300); //schimba dimensiunea Se observ c valorile variabilelor pot modicate indirect prin intera a mediul metodelor sale. Programarea orientat obiect descurajeaz folosirea a a direct a variabilelor unui obiect deoarece acesta poate adus stri ina n a consistente (ireale). In schimb, pentru ecare variabil care descrie starea a

38

CAPITOLUL 2. OBIECTE SI CLASE

obiectului trebuie s existe metode care s permit schimbarea/aarea vala a a orilor variabilelor sale. Acestea se numesc metode de accesare, sau metode setter - getter i au numele de forma setVariabila, respectiv getVariabila. s patrat.width = -100; //stare inconsistenta patrat.setSize(-100, -200); //metoda setter //metoda setSize poate sa testeze daca noile valori sunt //corecte si sa valideze sau nu schimbarea lor

2.1.3

Distrugerea obiectelor

Multe limbaje de programare impun ca programatorul s in evidenta obiectelor at a create i s le distrug mod explicit atunci cnd nu mai este nevoie de ele, s a a n a cu alte cuvinte s administreze singur memoria ocupat de obiectele sale. a a Practica a demonstrat c aceast tehnic este una din principalele furnizoare a a a de erori ce duc la functionarea defectuoas a programelor. a In Java programatorul nu mai are responsabilitatea distrugerii obiectelor sale ntruct, momentul rulrii unui program, simultan cu interpretorul a n a Java, ruleaz i un proces care se ocup cu distrugerea obiectelor care nu a s a mai sunt folosite. Acest proces pus la dispozitie de platforma Java de lucru se numete garbage collector (colector de gunoi), prescurtat gc. s Un obiect este eliminat din memorie de procesul de colectare atunci cnd a nu mai exist nici o referint la acesta. Referintele (care sunt de fapt varia a abile) sunt distruse dou moduri: a natural, atunci cnd variabila respectiv iese din domeniul su de viza a a ibilitate, de exemplu la terminarea metodei care ea a fost declarat; n a explicit, dac atribuim variabilei respective valoare null. a

Cum functioneaz colectorul de gunoaie ? a Colectorul de gunoaie este un proces de prioritate scazut care se exea cut periodic, scaneaz dinamic memoria ocupat de programul Java aat a a a executie i marcheaz acele obiecte care au referinte directe sau indirecte. n s a Dup ce toate obiectele au fost parcurse, cele care au rmas nemarcate sunt a a eliminate automat din memorie.

2.2. CREAREA CLASELOR

39

Apelul metodei gc din clasa System sugereaz mainii virtuale Java s a s a depun eforturi recuperarea memoriei ocupate de obiecte care nu mai a n sunt folosite, fr a forta a pornirea procesului. aa ns

Finalizare Inainte ca un obiect s e eliminat din memorie, procesul gc d acelui a a obiect posibilitatea s curete dup el, apelnd metoda de nalizare a obieca a a tului respectiv. Uzual, timpul nalizrii un obiect si inchide sierele i n a s socket-urile folosite, distruge referintele ctre alte obiecte (pentru a usura a s sarcina colectorului de gunoaie), etc. Codul pentru nalizarea unui obiect trebuie scris ntr-o metod special a a numita finalize a clasei ce descrie obiectul respectiv. (vezi Clasa Object)

Atentie Nu confundati metoda nalize din Java cu destructorii din C++. Metoda nalize nu are rolul de a distruge obiectul ci este apelat automat a nainte de eliminarea obiectului respectiv din memorie.

2.2
2.2.1

Crearea claselor
Declararea claselor

Clasele reprezint o modalitate de a introduce noi tipuri de date a ntr-o aplicatie Java, cealalt modalitate ind prin intermediul interfetelor. Declararea a unei clase respect urmtorul format general: a a [public][abstract][final]class NumeClasa [extends NumeSuperclasa] [implements Interfata1 [, Interfata2 ... ]] { // Corpul clasei } Aadar, prima parte a declaratiei o ocup modicatorii clasei. Acetia s a s sunt:

40

CAPITOLUL 2. OBIECTE SI CLASE public Implicit, o clas poate folosit doar de clasele aate acelai paa a n s chet(librrie) cu clasa respectiv (dac nu se specic un anume paa a a a chet, toate clasele din directorul curent sunt considerate a acelai n s pachet). O clas declarat cu public poate folosit din orice alt a a a a clas, indiferent de pachetul care se gsete. a n a s abstract Declar o clas abstract (ablon). O clas abstract nu poate a a a s a a instantiat, ind folosit doar pentru a crea un model comun pentru o a a serie de subclase. (vezi Clase i metode abstracte) s nal Declar c respectiva clas nu poate avea subclase. Declarare claselor a a a nale are dou scopuri: a securitate: unele metode pot atepta ca parametru un obiect al s unei anumite clase i nu al unei subclase, dar tipul exact al unui s obiect nu poate aat cu exactitate decat momentul executiei; n felul acesta nu s-ar mai putea realiza obiectivul limbajului Java n ca un program care a trecut compilarea s nu mai e susceptibil a de nici o eroare. programare n spririt orientat-obiect: O clasa perfect nu tre a buie s mai aib subclase. a a

Dup numele clasei putem specica, dac este cazul, faptul c respectiva a a a clas este subclas a unei alte clase cu numele NumeSuperclasa sau/i c a a s a implementeaz una sau mai multe interfete, ale cror nume trebuie separate a a prin virgul. a

2.2.2

Extinderea claselor

Spre deosebire de alte limbaje de programare orientate-obiect, Java permite doar motenirea simpl, ceea ce s a neamn c o clas poate avea un singur a a a printe (superclas). Evident, o clas poate avea oricti motenitori (suba a a a s clase), de unde rezult c multimea tuturor claselor denite Java poate a a n vazut ca un arbore, rdcina acestuia ind clasa Object. Aadar, Object a a a s este singura clas care nu are printe, ind foarte important modul de a a a n lucru cu obiecte si structuri de date Java. n

2.2. CREAREA CLASELOR Extinderea unei clase se realizeaz folosind cuvntul cheie extends: a a class B extends A {...} // A este superclasa clasei B // B este o subclasa a clasei A

41

O subclas motenete de la printele su toate variabilele i metodele a s s a a s care nu sunt private.

2.2.3

Corpul unei clase

Corpul unei clase urmeaz imediat dup declararea clasei i este cuprins a a s ntre acolade. Continutul acestuia este format din: Declararea i, eventual, initializarea variabilelor de instant i de clas s a s a (cunoscute mpreun ca variabile membre). a Declararea i implementarea constructorilor. s Declararea i implementarea metodelor de instanta i de clas (cunoss s a cute mpreun ca metode membre). a Declararea unor clase imbricate (interne). Spre deosebire de C++, nu este permis doar declararea metodei corpul a n clasei, urmnd ca implementare s e facut afara ei. Implementarea a a a n metodelor unei clase trebuie s se fac obligatoriu corpul clasei. a a n // C++ class A { void metoda1(); int metoda2() { // Implementare } } A::metoda1() { // Implementare }

42 // Java class A { void metoda1(){ // Implementare } void metoda2(){ // Implementare } }

CAPITOLUL 2. OBIECTE SI CLASE

Variabilele unei clase pot avea acelai nume cu metodele clasei, care poate s chiar numele clasei, fr a exista posibilitatea aparitiei vreunei ambiguiti aa at din punctul de vedere al compilatorului. Acest lucru este a total nerens comandat dac ne gndim din perspectiva lizibilitii (claritii) codului, a a at at dovedind un stil inecient de progamare. class A { int A; void A() {}; // Corect pentru compilator // Nerecomandat ca stil de programare }

Atentie Variabilele i metodele nu pot avea ca nume un cuvnt cheie Java. s a

2.2.4

Constructorii unei clase

Constructorii unei clase sunt metode speciale care au acelai nume cu cel s al clasei, nu returneaz nici o valoare i sunt folositi pentru initializarea a s obiectelor acelei clase momentul instantierii lor. n class NumeClasa { [modificatori] NumeClasa([argumente]) { // Constructor

2.2. CREAREA CLASELOR } }

43

O clas poate avea unul sau mai multi constructori care trebuie a s a ns a difere prin lista de argumente primite. In felul acesta sunt permise diverse tipuri de initializri ale obiectelor la crearea lor, functie de numrul para a n a metrilor cu care este apelat constructorul. S considerm ca exemplu declararea unei clase care descrie notiunea de a a dreptunghi i trei posibili constructori pentru aceasta clas. s a class Dreptunghi { double x, y, w, h; Dreptunghi(double x1, double y1, double w1, double h1) { // Cel mai general constructor x=x1; y=y1; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi(double w1, double h1) { // Constructor cu doua argumente x=0; y=0; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi() { // Constructor fara argumente x=0; y=0; w=0; h=0; System.out.println("Instantiere dreptunghi"); } } Constructorii sunt apelati automat la instantierea unui obiect. In cazul care dorim s apelm explicit constructorul unei clase folosim expresia n a a this( argumente ), care apeleaz constructorul corespunztor (ca argumente) al clasei respeca a tive. Aceast metod este folosit atunci cnd sunt implementati mai multi a a a a constructori pentru o clas, pentru a nu repeta secventele de cod scrise deja a la constructorii cu mai multe argumente (mai generali). Mai ecient, fr aa

44

CAPITOLUL 2. OBIECTE SI CLASE

a repeta aceleai secvente de cod toti constructorii (cum ar aarea s n s mesajului Instantiere dreptunghi), clasa de mai sus poate rescris astfel: a class Dreptunghi { double x, y, w, h; Dreptunghi(double x1, double y1, double w1, double h1) { // Implementam doar constructorul cel mai general x=x1; y=y1; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi(double w1, double h1) { this(0, 0, w1, h1); // Apelam constructorul cu 4 argumente } Dreptunghi() { this(0, 0); // Apelam constructorul cu 2 argumente } } Dintr-o subclas putem apela explicit constructorii superclasei cu expresia a super( argumente ). S presupunem c dorim s crem clasa Patrat, derivat din clasa Dreptunghi: a a a a a class Patrat extends Dreptunghi { Patrat(double x, double y, double d) { super(x, y, d, d); // Apelam constructorul superclasei } }

Atentie Apelul explcit al unui constructor nu poate aprea dect a a ntr-un alt constructor si trebuie s e prima instructiune din constructorul respectiv. a

2.2. CREAREA CLASELOR

45

Constructorul implicit Constructorii sunt apelati automat la instantierea unui obiect. In cazul care scriem o clas care nu are declarat nici un constructor, sistemul n a i creeaz automat un constructor implicit, care nu primete nici un argument a s i care nu face nimic. Deci prezenta constructorilor corpul unei clase nu s n este obligatorie. Dac a scriem un constructor pentru o clas, care are mai a ns a mult de un argument, atunci constructorul implicit (fr nici un argument) aa nu va mai furnizat implicit de ctre sistem. S considerm, ca exemplu, a a a urmtoarele declaratii de clase: a class Dreptunghi { double x, y, w, h; // Nici un constructor } class Cerc { double x, y, r; // Constructor cu 3 argumente Cerc(double x, double y, double r) { ... }; } S considerm acum dou instantieri ale claselor de mai sus: a a a Dreptunghi d = new Dreptunghi(); // Corect (a fost generat constructorul implicit) Cerc c; c = new Cerc(); // Eroare la compilare ! c = new Cerc(0, 0, 100); // Varianta corecta In cazul motenirii unei clase, instantierea unui obiect din clasa extins s a implic instantierea unui obiect din clasa printe. Din acest motiv, ecare a a constructor al clasei u va trebui s aib un constructor cu aceeai signatur a a s a printe sau s apeleze explicit un constructor al clasei extinse folosind n a a expresia super([argumente]), caz contrar ind semnalat o eroare la n a compilare.

46 class A { int x=1; A(int x) { this.x = x;} } class B extends A { // Corect B() {super(2);} B(int x) {super.x = x;} } class C extends A { // Eroare la compilare ! C() {super.x = 2;} C(int x) {super.x = x;} }

CAPITOLUL 2. OBIECTE SI CLASE

Constructorii unei clase pot avea urmtorii modicatori de acces: a public, protected, private i cel implicit. s public In orice alt clas se pot crea instante ale clasei respective. a a protected Doar subclase pot create obiecte de tipul clasei respective. n private In nici o alt clas nu se pot instantia obiecte ale acestei clase. O asta a fel de clas poate contine metode publice (numite factory methods) a care s e responsabile cu crearea obiectelor, controlnd felul acesta a a n diverse aspecte legate de instantierea clasei respective. implicit Doar clasele din acelai pachet se pot crea instante ale clasei respecn s tive.

2.2.5

Declararea variabilelor

Variabilele membre ale unei clase se declar de obicei a naintea metodelor, dei acest lucru nu este impus de ctre compilator. s a

2.2. CREAREA CLASELOR class NumeClasa { // Declararea variabilelor // Declararea metodelor }

47

Variabilele membre ale unei clase se declar corpul clasei i nu corpul a n s n unei metode, ind vizibile toate metodele respectivei clase. Variabilele n declarate cadrul unei metode sunt locale metodei respective. n Declararea unei variabile presupune specicarea urmtoarelor lucruri: a numele variabilei tipul de date al acesteia nivelul de acces la acea variabila din alte clase dac este constant sau nu a a dac este variabil de instant sau de clas a a a a alti modicatori Generic, o variabil se declar astfel: a a [modificatori] Tip numeVariabila [ = valoareInitiala ]; unde un modicator poate : un modicator de acces : public, protected, private (vezi Modicatori de acces pentru membrii unei clase) unul din cuvintele rezervate: static, final, transient, volatile Exemple de declaratii de variabile membre: class Exemplu { double x; protected static int n; public String s = "abcd"; private Point p = new Point(10, 10); final static long MAX = 100000L; }

48

CAPITOLUL 2. OBIECTE SI CLASE

S analizm modicatorii care pot specicati pentru o variabil, altii a a a dect cei de acces care sunt tratati a ntr-o sectiune separata: Specicatori de acces pentru membrii unei clase. static Prezenta lui declar c o variabil este variabil de clas i nu de a a a a a s instant. (vezi Membri de instanta i membri de clas) a s a int variabilaInstanta ; static int variabilaClasa; nal Indic faptul c valoarea variabilei nu mai poate schimbat, cu alte a a a cuvinte este folosit pentru declararea constantelor. final double PI = 3.14 ; ... PI = 3.141; // Eroare la compilare ! Prin conventie, numele variabilelor nale se scriu cu litere mari. Folosirea lui final aduce o exibilitate sporit lucrul cu constante, sensul a n n c valoarea unei variabile nu trebuie specicat neaprat la declararea a a a ei (ca exemplul de mai sus), ci poate specicat i ulterior n as ntr-un constructor, dup care ea nu va mai putea modicat. a a class Test { final int MAX; Test() { MAX = 100; // Corect MAX = 200; // Eroare la compilare ! } } transient Este folosit la serializarea obiectelor, pentru a specica ce variabile membre ale unui obiect nu particip la serializare. (vezi Serializarea a obiectelor)

2.2. CREAREA CLASELOR

49

volatile Este folosit pentru a semnala compilatorului s nu execute anumite a optimizri asupra membrilor unei clase. Este o facilitate avansat a a a limbajului Java.

2.2.6

this i super s

Sunt variabile predenite care fac referinta, cadrul unui obiect, la obiectul n propriu-zis (this), respectiv la instanta printelui (super). Sunt folosite a general pentru a rezolva conicte de nume prin referirea explicit a unei n a variabile sau metode membre. Dup cum am vzut, utilizate sub form de a a a metode au rolul de a apela constructorii corespunztori ca argumente ai clasei a curente, respectiv ai superclasei class A { int x; A() { this(0); } A(int x) { this.x = x; } void metoda() { x ++; } } class B extends A { B() { this(0); } B(int x) { super(x); System.out.println(x); } void metoda() { super.metoda();

50 System.out.println(x); } }

CAPITOLUL 2. OBIECTE SI CLASE

2.3
2.3.1

Implementarea metodelor
Declararea metodelor

Metodele sunt responsabile cu descrierea comportamentului unui obiect. Intruct Java este un limbaj de programare complet orientat-obiect, metodele a se pot gsi doar cadrul claselor. Generic, o metod se declar astfel: a n a a [modificatori] TipReturnat numeMetoda ( [argumente] ) [throws TipExceptie1, TipExceptie2, ...] { // Corpul metodei } unde un modicator poate : un specicator de acces : public, protected, private (vezi Specicatori de acces pentru membrii unei clase) unul din cuvintele rezervate: static, abstract, final, native, synchronized S analizm modicatorii care pot specicati pentru o metod, altii a a a dect cei de acces care sunt tratati a ntr-o sectiune separat. a static Prezenta lui declar c o metod este de clas i nu de instant. (vezi a a a as a Membri de instanta i membri de clas) s a void metodaInstanta(); static void metodaClasa(); abstract Permite declararea metodelor abstracte. O metod abstract este o a a metod care nu are implementare i trebuie obligatoriu s fac parte a s a a dintr-o clas abstract. (vezi Clase i metode abstracte) a a s

2.3. IMPLEMENTAREA METODELOR

51

nal Specic faptul c acea metoda nu mai poate supradenit suba a a n clasele clasei care ea este denit ca ind nal. Acest lucru este n a a util dac respectiva metod are o implementare care nu trebuie schima a bat sub nici o form subclasele ei, ind critic pentru consistenta a a n a strii unui obiect. De exemplu, studentilor unei universiti trebuie s a at a li se calculeze media nala, functie de notele obtinute la examene, n aceeai manier, indiferent de facultatea la care sunt. n s a

class Student { ... final float calcMedie(float note[], float ponderi[]) { ... } ... } class StudentInformatica extends Student { float calcMedie(float note[], float ponderi[]) { return 10.00; } }// Eroare la compilare !

native In cazul care avem o librrie important de functii scrise alt limbaj n a a n de programare, cum ar C, C++ i limbajul de asamblare, acestea s pot refolosite din programele Java. Tehnologia care permite acest lucru se numete JNI (Java Native Interface) i permite asocierea dintre s s metode Java declarate cu native i metode native scrise limbajele s n de programare mentionate. synchronized Este folosit cazul care se lucreaz cu mai multe re de executie iar n n a metoda respectiv gestioneaz resurse comune. Are ca efect construirea a a unui monitor care nu permite executarea metodei, la un moment dat, dect unui singur r de executie. (vezi Fire de executie) a

52

CAPITOLUL 2. OBIECTE SI CLASE

2.3.2

Tipul returnat de o metod a

Metodele pot sau nu s returneze o valoare la terminarea lor. Tipul returnat a poate att un tip primitiv de date sau o referint la un obiect al unei clase. a a In cazul care o metod nu returneaz nimic atunci trebuie obligatoriu n a a specicat cuvntul cheie void ca tip returnat: a public void afisareRezultat() { System.out.println("rezultat"); } private void deseneaza(Shape s) { ... return; } Dac o metod trebuie s returneze o valoare acest lucru se realizeaz prin a a a a intermediul instructiunii return, care trebuie s apar toate situatiile de a a n terminare a functiei. double radical(double x) { if (x >= 0) return Math.sqrt(x); else { System.out.println("Argument negativ !"); // Eroare la compilare // Lipseste return pe aceasta ramura } } In cazul care declaratia functiei tipul returnat este un tip primitiv de n n date, valoarea returnat la terminarea functiei trebuie s aib obligatoriu acel a a a tip sau un subtip al su, altfel va furnizat o eroare la compilare. In general, a a orice atribuire care implic pierderi de date este tratat de compilator ca a a eroare. int metoda() { return 1.2; // Eroare } int metoda() {

2.3. IMPLEMENTAREA METODELOR return (int)1.2; // Corect } double metoda() { return (float)1; // Corect }

53

Dac valoarea returnat este o referint la un obiect al unei clase, atunci a a a clasa obiectului returnat trebuie s coincid sau s e o subclas a clasei a a a a specicate la declararea metodei. De exemplu, e clasa Poligon i subclasa s acesteia Patrat. Poligon metoda1( ) { Poligon p = new Poligon(); Patrat t = new Patrat(); if (...) return p; // Corect else return t; // Corect } Patrat metoda2( ) { Poligon p = new Poligon(); Patrat t = new Patrat(); if (...) return p; // Eroare else return t; // Corect }

2.3.3

Trimiterea parametrilor ctre o metod a a

Signatura unei metode este dat de numarul i tipul argumentelor primite a s de acea metod. Tipul de date al unui argument poate orice tip valid al a limbajului Java, att tip primitiv ct i tip referint. a a s a TipReturnat metoda([Tip1 arg1, Tip2 arg2, ...]) Exemplu:

54

CAPITOLUL 2. OBIECTE SI CLASE void adaugarePersoana(String nume, int varsta, float salariu) // String este tip referinta // int si float sunt tipuri primitive

Spre deosebire de alte limbaje, Java nu pot trimise ca parametri ai n unei metode referinte la alte metode (functii), a pot trimise referinte la ns obiecte care s contin implementarea acelor metode, pentru a apelate. a a Pna la aparitia versiunii 1.5, Java o metod nu putea primi un numr a n a a variabil de argumente, ceea ce nseamna c apelul unei metode trebuia s se a a fac cu specicarea exact a numarului i tipurilor argumentelor. Vom anala a s iza ntr-o sectiune separat modalitate de specicare a unui numr variabil a a de argumente pentru o metod. a Numele argumentelor primite trebuie s difere a ntre ele i nu trebuie s cos a incid cu numele nici uneia din variabilele locale ale metodei. Pot a s a ns a coincid cu numele variabilelor membre ale clasei, caz care diferentierea a n dintre ele se va face prin intermediul variabile this. class Cerc { int x, y, raza; public Cerc(int x, int y, int raza) { this.x = x; this.y = y; this.raza = raza; } } In Java argumentele sunt trimise doar prin valoare (pass-by-value). Acest lucru nseamn c metoda receptioneaz doar valorile variabilelor prima a a ite ca parametri. Cnd argumentul are tip primitiv de date, metoda nu-i poate schimba vala oarea dect local ( cadrul metodei); la revenirea din metod variabila are a n a aceeai valoare ca s naintea apelului, modicrile fcute cadrul metodei a a n ind pierdute. Cnd argumentul este de tip referint, metoda nu poate schimba valoarea a a referintei obiectului, a poate apela metodele acelui obiect i poate modica ns s orice variabil membr accesibil. a a a Aadar, dac dorim ca o metod s schimbe starea (valoarea) unui argus a a a ment primit, atunci el trebuie s e neaparat de tip referint. a a

2.3. IMPLEMENTAREA METODELOR

55

De exemplu, s considerm clasa Cerc descris anterior care dorim s a a a n a implementm o metod care s returneze parametrii cercului. a a a // Varianta incorecta: class Cerc { private int x, y, raza; public void aflaParametri(int valx, int valy, int valr) { // Metoda nu are efectul dorit! valx = x; valy = y; valr = raza; } } Aceast metod nu va realiza lucrul propus a a ntruct ea primete doar a s valorile variabilelor valx, valy i valr i nu referinte la ele (adresele lor s s de memorie), astfel at s le poat modica valorile. In concluzie, metoda nc a a nu realizeaz nimic pentru c nu poate schimba valorile variabilelor primite a a ca argumente. Pentru a rezolva lucrul propus trebuie s denim o clas suplimentar a a a care s descrie parametrii pe care dorim s-i am: a a a // Varianta corecta class Param { public int x, y, raza; } class Cerc { private int x, y, raza; public void aflaParametri(Param param) { param.x = x; param.y = y; param.raza = raza; } } Argumentul param are tip referint i, dei nu schimbm valoarea (vala s s i a oarea sa este adresa de memorie la care se gasete i nu poate schimbat), s s a

56

CAPITOLUL 2. OBIECTE SI CLASE

putem schimba starea obiectului, adic informatia propriu-zis continut de a a a acesta.

Varianta de mai sus a fost dat pentru a clarica modul de trimitere a a argumentelor unei metode. Pentru a aa a valorile variabilelor care descriu ns starea unui obiect se folosesc metode de tip getter ite de metode setter nsot care s permit schimbarea strii obiectului: a a a class Cerc { private int x, y, raza; public int getX() { return x; } public void setX(int x) { this.x = x; } ... }

2.3.4

Metode cu numr variabil de argumente a

Incepnd cu versiunea 1.5 a limbajului Java, exist posibilitate de a declara a a metode care s primeasc un numr variabil de argumente. Noutatea const a a a a folosirea simbolului ..., sintaxa unei astfel de metode ind: n [modificatori] TipReturnat metoda(TipArgumente ... args)

args reprezint un vector avnd tipul specicat i instantiat cu un numr a a s a variabil de argumente, functie de apelul metodei. Tipul argumentelor n poate referint sau primitiv. Metoda de mai jos aeaz argumentele prima s a ite, care pot de orice tip: void metoda(Object ... args) { for(int i=0; i<args.length; i++) System.out.println(args[i]); } ... metoda("Hello"); metoda("Hello", "Java", 1.5);

2.3. IMPLEMENTAREA METODELOR

57

2.3.5

Supra arcarea i supradenirea metodelor nc s

Supra arcarea i supradenirea metodelor sunt dou concepte extrem de nc s a utile ale programrii orientate obiect, cunoscute i sub denumirea de polimora s sm, i se refer la: s a suprancarcarea (overloading) : cadrul unei clase pot exista metode n cu acelai nume cu conditia ca signaturile lor s e diferite (lista de s a argumente primite s difere e prin numrul argumentelor, e prin a a tipul lor) astfel at la apelul functiei cu acel nume s se poat stabili nc a a mod unic care dintre ele se execut. n a supradenirea (overriding): o subclas poate rescrie o metod a claa a sei printe prin implementarea unei metode cu acelai nume i aceeai a s s s signatur ca ale superclasei. a class A { void metoda() { System.out.println("A: metoda fara parametru"); } // Supraincarcare void metoda(int arg) { System.out.println("A: metoda cu un parametru"); } } class B extends A { // Supradefinire void metoda() { System.out.println("B: metoda fara parametru"); } } O metod supradenit poate s: a a a ignore complet codul metodei corespunztoare din superclas (cazul a a de mai sus): B b = new B(); b.metoda(); // Afiseaza "B: metoda fara parametru"

58

CAPITOLUL 2. OBIECTE SI CLASE extind codul metodei printe, executnd a a a nainte de codul propriu i s functia printelui: a class B extends A { // Supradefinire prin extensie void metoda() { super.metoda(); System.out.println("B: metoda fara parametru"); } } . . . B b = new B(); b.metoda(); /* Afiseaza ambele mesaje: "A: metoda fara parametru" "B: metoda fara parametru" */

O metod nu poate supradeni o metod declarat nal clasa printe. a a a a n a Orice clas care nu este abstract trebuie obligatoriu s supradeneasc a a a a metodele abstracte ale superclasei (dac este cazul). In cazul care o clas a n a nu supradenete toate metodele abstracte ale printelui, ea ai este abs a nss stract i va trebui declarat ca atare. as a In Java nu este posibil supra arcarea operatorilor. a nc

2.4

Modicatori de acces

Modicatorii de acces sunt cuvinte rezervate ce controleaz accesul celora late clase la membrii unei clase. Specicatorii de acces pentru variabilele i metodele unei clase sunt: public, protected, private i cel implicit (la s s nivel de pachet), iar nivelul lor de acces este dat tabelul de mai jos: n Specicator Clasa Sublasa Pachet Oriunde private X protected X X* X public X X X X implicit X X

2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA

59

Aadar, dac nu este specicat nici un modicator de acces, implicit s a nivelul de acces este la nivelul pachetului. In cazul care declarm un n a membru protected atunci accesul la acel membru este permis din subclasele clasei care a fost declarat dar depinde i de pachetul care se gasete n s n s subclasa: dac sunt acelai pachet accesul este permis, dac nu sunt a n s a n acelai pachet accesul nu este permis dect pentru obiecte de tipul subclasei. s a Exemple de declaratii: private int secretPersonal; protected String secretDeFamilie; public Vector pentruToti; long doarIntrePrieteni; private void metodaInterna(); public String informatii();

2.5

Membri de instant i membri de clas a s a

O clas Java poate contine dou tipuri de variabile i metode : a a s de instant: declarate fr modicatorul static, specice ecrei a a a a instante create dintr-o clas i as de clas: declarate cu modicatorul static, specice clasei. a

2.5.1

Variabile de instant i de clas a s a

Cnd declarm o variabil membr fr modicatorul static, cum ar x a a a a aa n exemplul de mai jos: class Exemplu { int x ; //variabila de instanta } se declar de fapt o variabil de instant, ceea ce a a a nseamn c la ecare creare a a a unui obiect al clasei Exemplu sistemul aloc o zon de memorie separat a a a pentru memorarea valorii lui x. Exemplu o1 = new Exemplu(); o1.x = 100;

60

CAPITOLUL 2. OBIECTE SI CLASE Exemplu o2 = new Exemplu(); o2.x = 200; System.out.println(o1.x); // Afiseaza 100 System.out.println(o2.x); // Afiseaza 200

Aadar, ecare obiect nou creat va putea memora valori diferite pentru s variabilele sale de instant. a Pentru variabilele de clas (statice) sistemul aloc o singur zon de mema a a a orie la care au acces toate instantele clasei respective, ceea ce nseamn c a a dac un obiect modic valoarea unei variabile statice ea se va modica i a a s pentru toate celelalte obiecte. Deoarece nu depind de o anumit instanta a a unei clase, variabilele statice pot referite i sub forma: s NumeClasa.numeVariabilaStatica class Exemplu { int x ; // Variabila de static long n; // Variabila de } . . . Exemplu o1 = new Exemplu(); Exemplu o2 = new Exemplu(); o1.n = 100; System.out.println(o2.n); o2.n = 200; System.out.println(o1.n); System.out.println(Exemplu.n); // o1.n, o2.n si Exemplu.n sunt

instanta clasa

// Afiseaza 100 // Afiseaza 200 // Afiseaza 200 referinte la aceeasi valoare

Initializarea variabilelor de clas se face o singur dat, la arcarea a a a nc n memorie a clasei respective, i este realizat prin atribuiri obinuite: s a s class Exemplu { static final double PI = 3.14; static long nrInstante = 0; static Point p = new Point(0,0); }

2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA

61

2.5.2

Metode de instant i de clas a s a

Similar ca la variabile, metodele declarate fr modicatorul static sunt aa metode de instant iar cele declarate cu static sunt metode de clas (stata a ice). Diferenta ntre cele dou tipuri de metode este urmtoarea: a a metodele de instant opereaz att pe variabilele de instant ct i pe a a a a a s cele statice ale clasei; metodele de clas opereaz doar pe variabilele statice ale clasei. a a class Exemplu { int x ; // Variabila de instanta static long n; // Variabila de clasa void metodaDeInstanta() { n ++; // Corect x --; // Corect } static void metodaStatica() { n ++; // Corect x --; // Eroare la compilare ! } } Intocmai ca i la variabilele statice, s ntruct metodele de clas nu depind a a de starea obiectelor clasei respective, apelul lor se poate face i sub forma: s NumeClasa.numeMetodaStatica Exemplu.metodaStatica(); // Corect, echivalent cu Exemplu obj = new Exemplu(); obj.metodaStatica(); // Corect, de asemenea Metodele de instant nu pot apelate dect pentru un obiect al clasei a a respective: Exemplu.metodaDeInstanta(); // Eroare la compilare ! Exemplu obj = new Exemplu(); obj.metodaDeInstanta(); // Corect

62

CAPITOLUL 2. OBIECTE SI CLASE

2.5.3

Utilitatea membrilor de clas a

Membrii de clas sunt folositi pentru a pune la dispozitie valori i metode a s independente de starea obiectelor dintr-o anumita clas. a

Declararea ecient a constantelor a S considerm situatia cnd dorim s declarm o constant. a a a a a a class Exemplu { final double PI = 3.14; // Variabila finala de instanta } La ecare instantiere a clasei Exemplu va rezervat o zon de memorie a a pentru variabilele nale ale obiectului respectiv, ceea ce este o risip a ntruct a aceste constante au aceleai valori pentru toate instantele clasei. Declararea s corect a constantelor trebuie aadar facut cu modicatorii static i final, a s a s pentru a le rezerva o singur zon de memorie, comun tuturor obiectelor: a a a class Exemplu { static final double PI = 3.14; // Variabila finala de clasa }

Numrarea obiectelor unei clase a Numrarea obiectelor unei clase poate fcut extrem de simplu folosind a a a o variabil static i este util situatiile cnd trebuie s controlm diveri a as a n a a a s parametri legati de crearea obiectelor unei clase. class Exemplu { static long nrInstante = 0; Exemplu() { // Constructorul este apelat la fiecare instantiere nrInstante ++; } }

2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA

63

Implementarea functiilor globale Spre deosebire de limbajele de programare procedurale, Java nu putem n avea functii globale denite ca atare, ntruct orice este un obiect. Din a acest motiv chiar i metodele care au o functionalitate global trebuie ims a plementate cadrul unor clase. Acest lucru se va face prin intermediul n metodelor de clas (globale), deoarece acestea nu depind de starea partica ular a obiectelor din clasa respectiv. De exemplu, s considerm functia a a a a sqrt care extrage radicalul unui numr i care se gsete clasa Math. Dac a s a s n a nu ar fost functie de clas, apelul ei ar trebuit fcut astfel (incorect, de a a altfel): // Incorect ! Math obj = new Math(); double rad = obj.sqrt(121); ceea ce ar fost extrem de neplcut... Fiind a metod static ea poate a ns a a apelat prin: Math.sqrt(121) . a Aadar, functiile globale necesare unei aplicatii vor grupate corespunztor s a diverse clase i implementate ca metode statice. n s

2.5.4

Blocuri statice de initializare

Variabilele statice ale unei clase sunt initializate la un moment care precede prima utilizare activ a clasei respective. Momentul efectiv depinde de ima plementarea mainii virtuale Java i poart numele de initializarea clasei. Pe s s a lng setarea valorilor variabilelor statice, aceast etap sunt executate i a a n a a s blocurile statice de initializare ale clasei. Acestea sunt secvente de cod de forma: static { // Bloc static de initializare; ... } care se comport ca o metod static apelat automat de ctre maina vira a a a a s tual. Variabilele referite a ntr-un bloc static de initializare trebuie s e a obligatoriu de clas sau locale blocului: a public class Test { // Declaratii de variabile statice

64 static int x = 0, y, z;

CAPITOLUL 2. OBIECTE SI CLASE

// Bloc static de initializare static { System.out.println("Initializam..."); int t=1; y = 2; z = x + y + t; } Test() { /* La executia constructorului variabilele de clasa sunt deja initializate si toate blocurile statice de initializare au fost obligatoriu executate in prealabil. */ ... } }

2.6
2.6.1

Clase imbricate
Denirea claselor imbricate

O clas imbricat este, prin denitie, o clas membr a unei alte clase, numit a a a a a i clas de acoperire. In functie de situatie, denirea unei clase interne se s a poate face e ca membru al clasei de acoperire - caz care este accesibil n a tuturor metodelor, e local cadrul unei metode. n class ClasaDeAcoperire{ class ClasaImbricata1 { // Clasa membru } void metoda() { class ClasaImbricata2 { // Clasa locala metodei } }

2.6. CLASE IMBRICATE }

65

Folosirea claselor imbricate se face atunci cnd o clas are nevoie ima a n plementarea ei de o alt clas i nu exist nici un motiv pentru care aceasta a as a din urm s e declarat de sine stttoare (nu mai este folosit nicieri). a a a aa a a O clas imbricat are un privilegiu special fat de celelalte clase i anume a a a s acces nerestrictionat la toate variabilele clasei de acoperire, chiar dac aces a tea sunt private. O clas declarat local unei metode va avea acces i la a a a s variabilele nale declarate metoda respectiv. n a class ClasaDeAcoperire{ private int x=1; class ClasaImbricata1 { int a=x; } void metoda() { final int y=2; int z=3; class ClasaImbricata2 { int b=x; int c=y; int d=z; // Incorect } } } O clas imbricat membr (care nu este local unei metode) poate a a a a referit din exteriorul clasei de acoperire folosind expresia a ClasaDeAcoperire.ClasaImbricata Aadar, clasele membru pot declarate cu modicatorii public, protected, s private pentru a controla nivelul lor de acces din exterior, ntocmai ca orice variabil sau metod mebr a clasei. Pentru clasele imbricate locale unei a a a metode nu sunt permii acesti modicatori. s Toate clasele imbricate pot declarate folosind modicatorii abstract i s final, semnicatia lor ind aceeai ca i cazul claselor obinuite. s s n s

66

CAPITOLUL 2. OBIECTE SI CLASE

2.6.2

Clase interne

Spre deosebire de clasele obinuite, o clas imbricat poate declarat static s a a a a sau nu. O clas imbricat nestatic se numete clasa intern. a a a s a class ClasaDeAcoperire{ ... class ClasaInterna { ... } static class ClasaImbricataStatica { ... } } Diferentierea acestor denumiri se face deoarece: o clas imbricat reect relatia sintactic a dou clase: codul unei a a a a a clase apare interiorul codului altei clase; n o clas intern reect relatia dintre instantele a dou clase, sensul a a a a n c o instanta a unei clase interne nu poate exista decat cadrul unei a n instante a clasei de acoperire. In general, cele mai folosite clase imbricate sunt cele interne. Aadar, o clas intern este o clas imbricat ale carei instante nu pot s a a a a exista dect cadrul instantelor clasei de acoperire i care are acces direct a n s la toti membrii clasei sale de acoperire.

2.6.3

Identicare claselor imbricate

Dup cum tim orice clas produce la compilare aa numitele uniti de coma s a s at pilare, care sunt iere avnd numele clasei respective i extensia .class s a s i care contin toate informatiile despre clasa respectiv. Pentru clasele ims a bricate aceste uniti de compilare sunt denumite astfel: numele clasei de at acoperire, urmat de simbolul $ apoi de numele clasei imbricate. class ClasaDeAcoperire{ class ClasaInterna1 {} class ClasaInterna2 {} }

2.7. CLASE SI METODE ABSTRACTE Pentru exemplul de mai sus vor generate trei iere: s ClasaDeAcoperire.class ClasaDeAcoperire$ClasaInterna1.class ClasaDeAcoperire$ClasaInterna2.class

67

In cazul care clasele imbricate au la rndul lor alte clase imbricate n a (situatie mai putin uzual) denumirea lor se face dup aceeai regul: adugarea a a s a a unui $ i apoi numele clasei imbricate. s

2.6.4

Clase anonime

Exist posibilitatea denirii unor clase imbricate locale, fr nume, utilizate a aa doar pentru instantierea unui obiect de un anumit tip. Astfel de clase se numesc clase anonime i sunt foarte utile situatii cum ar crearea unor s n obiecte ce implementeaz o anumit interfat sau extind o anumit clas a a a a a abstract. a Exemple de folosire a claselor anonime vor date capitolul Interfete, n precum i extensiv capitolul Interfata grac cu utilizatorul. s n a Fiierele rezultate urma compilrii claselor anonime vor avea numele s n a de forma ClasaAcoperire.$1,..., ClasaAcoperire.$n, unde n este numrul a de clase anonime denite clasa respectiv de acoperire. n a

2.7

Clase i metode abstracte s

Uneori proiectarea unei aplicatii este necesar s reprezentm cu ajutorul n a a claselor concepte abstracte care s nu poat instantiate i care s foloseasc a a s a a doar la dezvoltarea ulterioar a unor clase ce descriu obiecte concrete. De exa emplu, pachetul java.lang exist clasa abstract Number care modeleaz n a a a conceptul generic de numr. Intr-un program nu avem a nevoie de nua ns mere generice ci de numere de un anumit tip: ntregi, reale, etc. Clasa Number servete ca superclas pentru clasele concrete Byte, Double, Float, Integer, s a Long i Short, ce implementeaz obiecte pentru descrierea numerelor de un s a anumit tip. Aadar, clasa Number reprezint un concept abstract i nu vom s a s putea instantia obiecte de acest tip - vom folosi schimb subclasele sale. n Number numar = new Number(); // Eroare Integer intreg = new Integer(10); // Corect

68

CAPITOLUL 2. OBIECTE SI CLASE

2.7.1

Declararea unei clase abstracte

Declararea unei clase abstracte se face folosind cuvntul rezervat abstract: a [public] abstract class ClasaAbstracta [extends Superclasa] [implements Interfata1, Interfata2, ...] { // Declaratii uzuale // Declaratii de metode abstracte } O clas abstract poate avea modicatorul public, accesul implicit ind a a la nivel de pachet, dar nu poate specica modicatorul final, combinatia abstract final ind semnalat ca eroare la compilare - de altfel, o clas a a declarat astfel nu ar avea nici o utilitate. a O clas abstract poate contine aceleai elemente membre ca o clas a a s a obinuit, la care se adaug declaratii de metode abstracte - fr nici o ims a a aa plementare.

2.7.2

Metode abstracte

Spre deosebire de clasele obinuite care trebuie s furnizeze implementri s a a pentru toate metodele declarate, o clas abstract poate contine metode fr a a aa nici o implementare. Metodele fara nici o implementare se numesc metode abstracte i pot aprea doar clase abstracte. In fata unei metode abstracte s a n trebuie s apar obligatoriu cuvntul cheie abstract, altfel va furnizat o a a a a eroare de compilare. abstract class ClasaAbstracta { abstract void metodaAbstracta(); // Corect void metoda(); // Eroare } In felul acesta, o clas abstract poate pune la dispozitia subclaselor sale a a un model complet pe care trebuie s-l implementeze, furniznd chiar implea a mentarea unor metode comune tuturor claselor i lsnd explicitarea altora s aa

2.7. CLASE SI METODE ABSTRACTE

69

ecrei subclase parte. a n Un exemplu elocvent de folosire a claselor i metodelor abstracte este des scrierea obiectelor grace ntr-o manier orientat-obiect. a a Obiecte grace: linii, dreptunghiuri, cercuri, curbe Bezier, etc Stri comune: pozitia(originea), dimensiunea, culoarea, etc a Comportament: mutare, redimensionare, desenare, colorare, etc. Pentru a folosi strile i comportamentele comune acestor obiecte avana s n tajul nostru putem declara o clas generic GraphicObject care s e sua a a perclas pentru celelalte clase. Metodele abstracte vor folosite pentru ima plementarea comportamentului specic ecrui obiect, cum ar desenarea a iar cele obinuite pentru comportamentul comun tuturor, cum ar schims barea originii. Implementarea clasei abstracte GraphicObject ar putea arta a astfel: abstract class GraphicObject { // Stari comune private int x, y; private Color color = Color.black; ... // Metode comune public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setColor(Color color) { this.color = color; } ... // Metode abstracte abstract void draw(); ... }

70

CAPITOLUL 2. OBIECTE SI CLASE

O subclas care nu este abstract a unei clase abstracte trebuie s furnizeze a a a obligatoriu implementri ale metodelor abstracte denite superclas. Ima n a plementarea claselor pentru obiecte grace ar : class Circle extends GraphicObject { void draw() { // Obligatoriu implementarea ... } } class Rectangle extends GraphicObject { void draw() { // Obligatoriu implementarea ... } } Legat de metodele abstracte, mai trebuie mentionate urmtoarele: a O clas abstract poate s nu aib nici o metod abstract. a a a a a a O metod abstract nu poate aprea dect a a a a ntr-o clas abstract. a a Orice clas care are o metod abstract trebuie declarat ca ind aba a a a stract. a

In API-ul oferit de platforma de lucru Java sunt numeroase exemple de ierarhii care folosesc la nivelele superioare clase abstracte. Dintre cele mai importante amintim: Number: superclasa abstract a tipurilor referint numerice a a Reader, Writer: superclasele abstracte ale uxurilor de intrare/ieire s pe caractere InputStream, OutputStream: superclasele abstracte ale uxurilor de intrare/ieire pe octeti s AbstractList, AbstractSet, AbstractMap: superclase abstracte pentru structuri de date de tip colectie

2.8. CLASA OBJECT

71

Component : superclasa abstract a componentelor folosite deza n voltarea de aplicatii cu interfat grac cu utilizatorul (GUI), cum ar a a Frame, Button, Label, etc. etc.

2.8
2.8.1

Clasa Object
Orice clas are o superclas a a

Dup cum am vzut sectiunea dedicat modalitii de creare a unei clase, a a n a at clauza extends specic faptul c acea clas extinde (motenete) o alt a a a s s a clas, numit superclas. O clas poate avea o singur superclas (Java nu a a a a a a suport motenirea multipl) i chiar dac nu specicm clauza extends la a s a s a a crearea unei clase ea totui va avea o superclas. Cu alte cuvinte, Java s a n orice clas are o superclas i numai una. Evident, trebuie s existe o exceptie a as a de la aceast regul i anume clasa care reprezint rdcina ierarhiei format a as a a a a de relatiile de motenire dintre clase. Aceasta este clasa Object. s Clasa Object este i superclasa implicit a claselor care nu specic o s a a anumit superclas. Declaratiile de mai jos sunt echivalente: a a class Exemplu {} class Exemplu extends Object {}

2.8.2

Clasa Object

Clasa Object este cea mai general dintre clase, orice obiect ind, direct a sau indirect, descendent al acestei clase. Fiind printele tuturor, Object a denete i implementeaz comportamentul comun al tuturor celorlalte clase s s a Java, cum ar : posibilitatea testrii egalitii valorilor obiectelor, a at specicarea unei reprezentri ca ir de caractere a unui obiect , a s returnarea clasei din care face parte un obiect, noticarea altor obiecte c o variabil de conditie s-a schimbat, etc. a a

72

CAPITOLUL 2. OBIECTE SI CLASE

Fiind subclas a lui Object, orice clas poate supradeni metodele a a i care nu sunt nale. Metodele cel mai uzual supradenite sunt: clone, equals/hashCode, finalize, toString. clone Aceast metod este folosit pentru duplicarea obiectelor (crearea unor a a a clone). Clonarea unui obiect presupune crearea unui nou obiect de acelai tip i care s aib aceeai stare (aceleai valori pentru variabilele s s a a s s sale). equals, hashCode Acestea sunt, de obicei, supradenite mpreun. In metoda equals este a scris codul pentru compararea egalitii continutului a dou obiecte. at a Implicit (implementarea din clasa Object), aceast metod compar a a a referintele obiectelor. Uzual este redenit pentru a testa dac strile a a a obiectelor coincid sau dac doar o parte din variabilele lor coincid. a Metoda hashCode returneaza un cod ntreg pentru ecare obiect, pentru a testa consistenta obiectelor: acelai obiect trebuie s returneze s a acelai cod pe durata executiei programului. s Dac dou obiecte sunt egale conform metodei equals, atunci apelul a a metodei hashCode pentru ecare din cele dou obiecte ar trebui s a a returneze acelai intreg. s nalize In aceast metod se scrie codul care cur dup un obiect a a ata a nainte de a eliminat din memorie de colectorul de gunoaie. (vezi Distrugerea obiectelor) toString Este folosit pentru a returna o reprezentare ca ir de caractere a unui a s obiect. Este util pentru concatenarea irurilor cu diverse obiecte a s n vederea arii, ind apelat automat atunci cnd este necesar transsa a a a formarea unui obiect ir de caractere. n s Exemplu obj = new Exemplu(); System.out.println("Obiect=" + obj); //echivalent cu System.out.println("Obiect=" + obj.toString());

2.8. CLASA OBJECT

73

S considerm urmtorul exemplu, care implementm partial clasa a a a n a numerelor complexe, i care vom supradeni metode ale clasei Object. s n De asemenea, vom scrie un mic program TestComplex care vom testa n metodele clasei denite. Listing 2.1: Clasa numerelor complexe
class Complex { private double a ; // partea reala private double b ; // partea imaginara public Complex ( double a , double b ) { this . a = a ; this . b = b ; } public Complex () { this (1 , 0) ; } public boolean equals ( Object obj ) { if ( obj == null ) return false ; if (!( obj instanceof Complex ) ) return false ; Complex comp = ( Complex ) obj ; return ( comp . a == a && comp . b == b ) ; } public Object clone () { return new Complex (a , b ) ; } public String toString () { String semn = ( b > 0 ? " + " : " -" ) ; return a + semn + b + " i " ; } public Complex aduna ( Complex comp ) { Complex suma = new Complex (0 , 0) ; suma . a = this . a + comp . a ; suma . b = this . b + comp . b ; return suma ; } }

74

CAPITOLUL 2. OBIECTE SI CLASE

public class TestComplex { public static void main ( String c []) Complex c1 = new Complex (1 ,2) ; Complex c2 = new Complex (2 ,3) ; Complex c3 = ( Complex ) c1 . clone () ; System . out . println ( c1 . aduna ( c2 ) ) ; System . out . println ( c1 . equals ( c2 ) ) ; System . out . println ( c1 . equals ( c3 ) ) ; } }

// 3.0 + 5.0 i // false // true

2.9

Conversii automate ntre tipuri

Dup cum vzut tipurile Java de date pot artie primitive i referint. a a mp n s a Pentru ecare tip primitiv exist o clas corespunztoare care permie lucrul a a a orientat obiect cu tipul respectiv. byte short int long float double char boolean Byte Short Integer Long Float Double Character Boolean

Fiecare din aceste clase are un constructor ce permite initializarea unui obiect avnd o anumit valoare primitiv i metode specializate pentru cona a as versia unui obiect tipul primitiv corespunztor, de genul tipPrimitivValue: n a Integer obi = new Integer(1); int i = obi.intValue(); Boolean obb = new Boolean(true); boolean b = obb.booleanValue(); Incepnd cu versiunea 1.5 a limbajului Java, atribuirile explicite a ntre tipuri primitve i referint sunt posibile, acest mecanism purtnd numele de s a a autoboxing, respectiv auto-unboxing. Conversia explicit va facut de ctre a a a compilator.

2.10. TIPUL DE DATE ENUMERARE // Doar de la versiunea 1.5 ! Integer obi = 1; int i = obi; Boolean obb = true; boolean b = obb;

75

2.10

Tipul de date enumerare

Incepnd cu versiunea 1.5 a limbajului Java, exist posibilitatea de a deni a a tipuri de date enumerare prin folosirea cuvntului cheie enum. Acest a a solutie simplic manevrarea grupurilor de constante, dup cum reiese din a a urmtorul exemplu: a public class CuloriSemafor { public static final int ROSU = -1; public static final int GALBEN = 0; public static final int VERDE = 1; } ... // Exemplu de utilizare if (semafor.culoare = CuloriSemafor.ROSU) semafor.culoare = CuloriSemafor.GALBEN); ... Clasa de mai sus poate rescris astfel: a public enum CuloriSemafor { ROSU, GALBEN, VERDE }; ... // Utilizarea structurii se face la fel ... if (semafor.culoare = CuloriSemafor.ROSU) semafor.culoare = CuloriSemafor.GALBEN); ... Compilatorul este responsabil cu transformarea unei astfel de structuri ntr-o clas corespunztoare. a a

76

CAPITOLUL 2. OBIECTE SI CLASE

Capitolul 3 Exceptii
3.1 Ce sunt exceptiile ?

Termenul exceptie este o prescurtare pentru eveniment exceptional i poate s denit ca un eveniment ce se produce timpul executiei unui program i n s care provoac a ntreruperea cursului normal al executiei acestuia. Exceptiile pot aprea din diverse cauze i pot avea nivele diferite de grav a s itate: de la erori fatale cauzate de echipamentul hardware pn la erori ce a a in strict de codul programului, cum ar accesarea unui element din afara t spatiului alocat unui vector. In momentul cnd o asemenea eroare se produce timpul executiei va a n generat un obiect de tip exceptie ce contine: informatii despre exceptia respectiv; a starea programului momentul producerii acelei exceptii. n public class Exemplu { public static void main(String args[]) { int v[] = new int[10]; v[10] = 0; //Exceptie ! System.out.println("Aici nu se mai ajunge..."); } } La rularea programului va generat o exceptie, programul se va opri a la instructiunea care a cauzat exceptia i se va aa un mesaj de eroare de s s genul: 77

78

CAPITOLUL 3. EXCEPTII "Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException :10 at Exceptii.main (Exceptii.java:4)"

Crearea unui obiect de tip exceptie se numete aruncarea unei exceptii s (throwing an exception). In momentul care o metod genereaz (arunc) n a a a o exceptie sistemul de executie este responsabil cu gsirea unei secvente de a cod dintr-o metod care s o trateze. Cutarea se face recursiv, a a a ncepnd cu a metoda care a generat exceptia i mergnd s a napoi pe linia apelurilor ctre a acea metod. a Secventa de cod dintr-o metod care trateaz o anumit exceptie se a a a numete analizor de exceptie (exception handler) iar interceptarea i tratarea s s ei se numete prinderea exceptiei (catch the exception). s Cu alte cuvinte, la aparitia unei erori este aruncat o exceptie iar cineva a trebuie s o prind pentru a o trata. Dac sistemul nu gasete nici un a a a s analizor pentru o anumit exceptie, atunci programul Java se oprete cu un a s mesaj de eroare ( cazul exemplului de mai sus mesajul Aici nu se mai n ajunge... nu va aat). s

Atentie In Java tratarea erorilor nu mai este o optiune ci o constrngere. In a aproape toate situatile, o secvent de cod care poate provoca exceptii trebuie a s specice modalitatea de tratare a acestora. a

3.2

Prinderea i tratarea exceptiilor s

Tratarea exceptiilor se realizeaz prin intermediul blocurilor de instructiuni a try, catch i finally. O secvent de cod care trateaz anumite exceptii s a a trebuie s arate astfel: a try { // Instructiuni care pot genera exceptii } catch (TipExceptie1 variabila) { // Tratarea exceptiilor de tipul 1

3.2. PRINDEREA SI TRATAREA EXCEPTIILOR } catch (TipExceptie2 variabila) { // Tratarea exceptiilor de tipul 2 } . . . finally { // Cod care se executa indiferent // daca apar sau nu exceptii }

79

S considerm urmtorul exemplu: citirea unui ier octet cu octet i a a a s s asarea lui pe ecran. Fr a folosi tratarea exceptiilor metoda responsabil aa a cu citirea ierului ar arta astfel: s a public static void citesteFisier(String fis) { FileReader f = null; // Deschidem fisierul System.out.println("Deschidem fisierul " + fis); f = new FileReader(fis); // Citim si afisam fisierul caracter cu caracter int c; while ( (c=f.read()) != -1) System.out.print((char)c); // Inchidem fisierul System.out.println("\\nInchidem fisierul " + fis); f.close(); } Aceast secvent de cod va furniza erori la compilare deoarece Java a a n tratarea erorilor este obligatorie. Folosind mecanismul exceptiilor metoda citeste si poate trata singur erorile care pot surveni pe parcursul executiei a sale. Mai jos este codul complte i corect al unui program ce aeaz pe ecran s s a continutul unui ier al crui nume este primit ca argument de la linia de s a comand. Tratarea exceptiilor este realizat complet chiar de ctre metoda a a a citeste.

80

CAPITOLUL 3. EXCEPTII Listing 3.1: Citirea unui sier - corect

import java . io .*; public class CitireFisier { public static void citesteFisier ( String fis ) { FileReader f = null ; try { // Deschidem fisierul System . out . println ( " Deschidem fisierul " + fis ) ; f = new FileReader ( fis ) ; // Citim si afisam fisierul caracter cu caracter int c ; while ( ( c = f . read () ) != -1) System . out . print (( char ) c ) ; } catch ( File Not F o u n d E x c e p t i o n e ) { // Tratam un tip de exceptie System . err . println ( " Fisierul nu a fost gasit ! " ) ; System . err . println ( " Exceptie : " + e . getMessage () ) ; System . exit (1) ; } catch ( IOException e ) { // Tratam alt tip de exceptie System . out . println ( " Eroare la citirea din fisier ! " ) ; e . printStackTrace () ; } finally { if ( f != null ) { // Inchidem fisierul System . out . println ( " \ nInchidem fisierul . " ) ; try { f . close () ; } catch ( IOException e ) { System . err . println ( " Fisierul nu poate fi inchis ! " ) ; e . printStackTrace () ; } } } } public static void main ( String args []) { if ( args . length > 0) citesteFisier ( args [0]) ; else

3.2. PRINDEREA SI TRATAREA EXCEPTIILOR


System . out . println ( " Lipseste numele fisierului ! " ) ; } }

81

Blocul try contine instructiunile de deschidere a unui ier i de citire s s dintr-un ier, ambele putnd produce exceptii. Exceptiile provocate de s a aceste instructiuni sunt tratate cele dou blocuri catch, cte unul pen n a a tru ecare tip de exceptie. Inchiderea ierului se face blocul nally, s n deoarece acesta este sigur c se va executa Fr a folosi blocul nally, a aa nchiderea ierului ar trebuit facut ecare situatie care ierul ar s a n n s fost deschis, ceea ce ar dus la scrierea de cod redundant. try { ... // Totul a decurs bine. f.close(); } ... catch (IOException e) { ... // A aparut o exceptie la citirea din fisier f.close(); // cod redundant } O problem mai delicat care trebuie semnalata aceasta situatie este a a n faptul c metoda close, responsabil cu a a nchiderea unui ier, poate provoca s la rndul su exceptii, de exemplu atunci cnd ierul mai este folosit i de a a a s s alt proces i nu poate s nchis. Deci, pentru a avea un cod complet corect trebuie s tratm i posibilitatea aparitiei unei exceptii la metoda close. a a s

Atentie Obligatoriu un bloc de instructiuni try trebuie s e urmat de unul a sau mai multe blocuri catch, functie de exceptiile provocate de acele n instructiuni sau (optional) de un bloc nally.

82

CAPITOLUL 3. EXCEPTII

3.3

Aruncarea exceptiilor

In cazul care o metod nu si asum responsabilitatea tratrii uneia sau n a a a mai multor exceptii pe care le pot provoca anumite instructiuni din codul su atunci ea poate s arunce aceste exceptii ctre metodele care o apea a a leaz, urmnd ca acestea s implementeze tratarea lor sau, la rndul lor, s a a a a a arunce mai departe exceptiile respective. Acest lucru se realizeaz prin specicarea declaratia metodei a clauzei a n throws: [modificatori] TipReturnat metoda([argumente]) throws TipExceptie1, TipExceptie2, ... { ... }

Atentie O metod care nu trateaz o anumit exceptie trebuie obligatoriu s o a a a a arunce.

In exemplul de mai sus dac nu facem tratarea exceptiilor cadrul a n metodei citeste atunci metoda apelant (main) va trebui s fac acest lucru: a a a Listing 3.2: Citirea unui sier
import java . io .*; public class CitireFisier { public static void citesteFisier ( String fis ) throws FileNotFoundException , IOException { FileReader f = null ; f = new FileReader ( fis ) ; int c ; while ( ( c = f . read () ) != -1) System . out . print (( char ) c ) ; f . close () ; }

3.3. ARUNCAREA EXCEPTIILOR

83

public static void main ( String args []) { if ( args . length > 0) { try { citesteFisier ( args [0]) ; } catch ( File NotFo undE x c ep ti on e ) { System . err . println ( " Fisierul nu a fost gasit ! " ) ; System . err . println ( " Exceptie : " + e ) ; } catch ( IOException e ) { System . out . println ( " Eroare la citirea din fisier ! " ) ; e . printStackTrace () ; } } else System . out . println ( " Lipseste numele fisierului ! " ) ; } }

Observati c, acest caz, nu mai putem diferentia exceptiile provocate de a n citirea din ier s de inchiderea ierului, ambele ind de tipul IOException. s s De asemenea, inchiderea ierului nu va mai facut situatia care s a n n apare o exceptie la citirea din ier. Este situatia care putem folosi blocul s n finally fr a folosi nici un bloc catch: aa

public static void citesteFisier(String fis) throws FileNotFoundException, IOException { FileReader f = null; try { f = new FileReader(numeFisier); int c; while ( (c=f.read()) != -1) System.out.print((char)c); } finally { if (f!=null) f.close(); } }

84

CAPITOLUL 3. EXCEPTII

Metoda apelant poate arunca la rndul su exceptiile mai departe ctre a a a a metoda care a apelat-o la rndul ei. Aceast antuire se termin cu metoda a a nl a main care, dac va arunca exceptiile ce pot aprea corpul ei, va determina a a n trimiterea exceptiilor ctre maina virtual Java. a s a public void metoda3 throws TipExceptie { ... } public void metoda2 throws TipExceptie { metoda3(); } public void metoda1 throws TipExceptie { metoda2(); } public void main throws TipExceptie { metoda1(); } Tratarea exceptiilor de ctre JVM se face prin terminarea programului i a s aarea informatiilor despre exceptia care a determinat acest lucru. Pentru s exemplul nostru, metoda main ar putea declarat astfel: a public static void main(String args[]) throws FileNotFoundException, IOException { citeste(args[0]); } Intotdeauna trebuie gsit compromisul optim a ntre tratarea local a exceptiilor a i aruncarea lor ctre nivelele superioare, astfel at codul s e ct mai clar s a nc a a i identicarea locului care a aprut exceptia s e ct mai uor de fcut. s n a a a s a

Aruncarea unei exceptii se poate face i implicit prin instructiunea throw s ce are formatul: throw exceptie, ca exemplele de mai jos: n throw new IOException("Exceptie I/O"); ... if (index >= vector.length) throw new ArrayIndexOutOfBoundsException(); ...

3.4. AVANTAJELE TRATARII EXCEPTIILOR catch(Exception e) { System.out.println("A aparut o exceptie); throw e; }

85

Aceast instructiune este folosit mai ales la aruncarea exceptiilor proprii. a a (vezi Crearea propriilor exceptii)

3.4

Avantajele tratrii exceptiilor a

Prin modalitatea sa de tratare a exceptiilor, Java are urmtoarele avantaje a fat de mecanismul traditional de tratare a erorilor: a Separarea codului pentru tratarea unei erori de codul care ea poate n s apar. a a Propagarea unei erori pn la un analizor de exceptii corespunztor. a a a Gruparea erorilor dup tipul lor. a

3.4.1

Separarea codului pentru tratarea erorilor

In programarea traditional tratarea erorilor se combin cu codul ce poate a a produce aparitia lor producnd aa numitul cod spaghetti. S considerm a s a a urmtorul exemplu: o functie care a ncarc un ier memorie: a s n citesteFisier { deschide fisierul; determina dimensiunea fisierului; aloca memorie; citeste fisierul in memorie; inchide fisierul; } Problemele care pot aprea la aceasta functie, aparent simpl, sunt de a a genul: Ce se ampl dac: ... ? nt a a ierul nu poate deschis s nu se poate determina dimensiunea ierului s

86 nu poate alocat sucient memorie a a nu se poate face citirea din ier s ierul nu poate s nchis

CAPITOLUL 3. EXCEPTII

Un cod traditional care s trateze aceste erori ar arta astfel: a a int citesteFisier() { int codEroare = 0; deschide fisierul; if (fisierul s-a deschis) { determina dimensiunea fisierului; if (s-a determinat dimensiunea) { aloca memorie; if (s-a alocat memorie) { citeste fisierul in memorie; if (nu se poate citi din fisier) { codEroare = -1; } } else { codEroare = -2; } } else { codEroare = -3; } inchide fisierul; if (fisierul nu s-a inchis && codEroare == 0) { codEroare = -4; } else { codEroare = codEroare & -4; } } else { codEroare = -5; } return codEroare; } // Cod "spaghetti" Acest stil de progamare este extrem de susceptibil la erori i s ngreuneaz a extrem de mult telegerea sa. In Java, folosind mecansimul exceptiilor, nt codul ar arata, schematizat, astfel:

3.4. AVANTAJELE TRATARII EXCEPTIILOR int citesteFisier() { try { deschide fisierul; determina dimensiunea fisierului; aloca memorie; citeste fisierul in memorie; inchide fisierul; } catch (fisierul nu s-a deschis) {trateaza eroarea;} catch (nu s-a determinat dimensiunea) {trateaza eroarea;} catch (nu s-a alocat memorie) {trateaza eroarea} catch (nu se poate citi din fisier) {trateaza eroarea;} catch (nu se poate inchide fisierul) {trateaza eroarea;} } Diferenta de claritate este evident. a

87

3.4.2

Propagarea erorilor

Propagarea unei erori se face pn la un analizor de exceptii corespunztor. a a a S presupunem c apelul la metoda citesteFisier este consecinta unor a a apeluri imbricate de metode: int metoda1() { metoda2(); ... } int metoda2() { metoda3; ... } int metoda3 { citesteFisier(); ...

88 }

CAPITOLUL 3. EXCEPTII

S presupunem de asemenea c dorim s facem tratarea erorilor doar a a a metoda1. Traditional, acest lucru ar trebui fcut prin propagarea erorii n a produse de metoda citesteFisier pn la metoda1: a a int metoda1() { int codEroare = metoda2(); if (codEroare != 0) //proceseazaEroare; ... } int metoda2() { int codEroare = metoda3(); if (codEroare != 0) return codEroare; ... } int metoda3() { int codEroare = citesteFisier(); if (codEroare != 0) return codEroare; ... } Dup cum am vazut, Java permite unei metode s arunce exceptiile a a aprute cadrul ei la un nivel superior, adic functiilor care o apeleaz a n a a sau sistemului. Cu alte cuvinte, o metod poate s nu si asume responsabila a itatea tratrii exceptiilor aprute cadrul ei: a a n int metoda1() { try { metoda2(); } catch (TipExceptie e) { //proceseazaEroare; } ... }

3.4. AVANTAJELE TRATARII EXCEPTIILOR int metoda2() throws TipExceptie { metoda3(); ... } int metoda3() throws TipExceptie { citesteFisier(); ... }

89

3.4.3

Gruparea erorilor dup tipul lor a

In Java exist clase corespunztoare tuturor exceptiilor care pot aprea la a a a executia unui program. Acestea sunt grupate functie de similaritile n at lor ntr-o ierarhie de clase. De exemplu, clasa IOException se ocup cu a exceptiile ce pot aprea la operatii de intrare/iesire i diferentiaz la rndul a s a a ei alte tipuri de exceptii, cum ar FileNotFoundException, EOFException, etc. La rndul ei, clasa IOException se a ncadreaz a ntr-o categorie mai larg de a exceptii i anume clasa Exception. s Radacin acestei ierarhii este clasa Throwable (vezi Ierarhia claselor ce dea scriu exceptii). Pronderea unei exceptii se poate face e la nivelul clasei specice pen tru acea exceptie, e la nivelul uneia din superclasele sale, functie de n necesitile programului, a, cu ct clasa folosit este mai generic cu att at ns a a a a tratarea exceptiilor programul si pierde din exibilitate. try { FileReader f = new FileReader("input.dat"); /* Acest apel poate genera exceptie de tipul FileNotFoundException Tratarea ei poate fi facuta in unul din modurile de mai jos: */ } catch (FileNotFoundException e) { // Exceptie specifica provocata de absenta // fisierului input.dat } // sau

90

CAPITOLUL 3. EXCEPTII catch (IOException e) { // Exceptie generica provocata de o operatie IO } // sau catch (Exception e) { // Cea mai generica exceptie soft } //sau catch (Throwable e) { // Superclasa exceptiilor }

3.5

Ierarhia claselor ce descriu exceptii

Rdcina claselor ce descriu exceptii este clasa Throwable iar cele mai impora a tante subclase ale sale sunt Error, Exception i RuntimeException, care s sunt la rndul lor superclase pentru o serie a ntreag de tipuri de exceptii. a

Erorile, obiecte de tip Error, sunt cazuri speciale de exceptii generate de functionarea anormal a echipamentului hard pe care ruleaz un pro a a gram Java i sunt invizibile programatorilor. Un program Java nu trebuie s s a trateze aparitia acestor erori i este improbabil ca o metod Java s provoace s a a asemenea erori.

3.6. EXCEPTII LA EXECUTIE

91

Exceptiile, obiectele de tip Exception, sunt exceptiile standard (soft) care trebuie tratate de ctre programele Java. Dup cum am mai zis tratarea acesa a tor exceptii nu este o optiune ci o constrngere. Exceptiile care pot scpa a a netratate descind din subclasa RuntimeException i se numesc exceptii la s executie. Metodele care sunt apelate uzual pentru un obiect exceptie sunt denite clasa Throwable i sunt publice, astfel at pot apelate pentru orice tip n s nc de exceptie. Cele mai uzuale sunt: getMessage - aeaz detaliul unei exceptii; s a printStackTrace - aeaz informatii complete despre exceptie i los a s calizarea ei; toString - metod motenit din clasa Object, care furnizeaz reprezentarea a s a a ca ir de caractere a exceptiei. s

3.6

Exceptii la executie

In general, tratarea exceptiilor este obligatorie Java. De la acest principu se n sustrag a aa numitele exceptii la executie sau, cu alte cuvinte, exceptiile ns s care provin strict din vina programatorului i nu generate de o anumit s a situatie extern, cum ar lipsa unui ier. a s Aceste exceptii au o superclas comun RuntimeException i acesata a a s n categorie sunt incluse exceptiile provocate de: operatii aritmetice ilegale ( artirea mp ntregilor la zero); ArithmeticException accesarea membrilor unui obiect ce are valoarea null; NullPointerException accesarea eronat a elementelor unui vector. a ArrayIndexOutOfBoundsException Exceptiile la executie pot aprea uriunde program i pot extrem a n s de numeroare iar ncercarea de prindere a lor ar extrem de anevoioas. a Din acest motiv, compilatorul permite ca aceste exceptii s rmn netratate, a a a a tratarea lor neind a ilegal. Reamintim a c, cazul aparitiei oricrui ns a ns a n a tip de exceptie care nu are un analizor corespunztor, programul va termi a nat.

92

CAPITOLUL 3. EXCEPTII int v[] = new int[10]; try { v[10] = 0; } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Atentie la indecsi!"); e.printStackTrace(); } // Corect, programul continua v[11] = 0; /* Nu apare eroare la compilare dar apare exceptie la executie si programul va fi terminat. */ System.out.println("Aici nu se mai ajunge...");

Imprtirea la 0 va genera o exceptie doar dac tipul numerelor artite a a mp este aritmetic ntreg. In cazul tipurilor reale (float i double) nu va s generat nici o exceptie, ci va furnizat ca rezultat o constant care poate a a , functie de operatie, Infinity, -Infinity, sau Nan. int a=1, int b=0; System.out.println(a/b); // Exceptie la executie ! double x=1, y=-1, z=0; System.out.println(x/z); // Infinity System.out.println(y/z); // -Infinity System.out.println(z/z); // NaN

3.7

Crearea propriilor exceptii

Adeseori poate aprea necesitatea crerii unor exceptii proprii pentru a pune a a evidenta cazuri speciale de erori provocate de metodele claselor unei librrii, n a cazuri care nu au fost prevazute ierarhia exceptiilor standard Java. n O exceptie proprie trebuie s se a ncadreze a ierarhia exceptiilor Java, ns n cu alte cuvinte clasa care o implementeaz trebuie s e subclas a uneia a a a deja existente aceasta ierarhie, preferabil una apropiat ca semnicatie, n a sau superclasa Exception.

3.7. CREAREA PROPRIILOR EXCEPTII public class ExceptieProprie extends Exception { public ExceptieProprie(String mesaj) { super(mesaj); // Apeleaza constructorul superclasei Exception } }

93

S considerm urmtorul exemplu, care crem o clas ce descrie partial a a a n a a o stiv de numere a ntregi cu operatiile de adugare a unui element, respec a tiv de scoatere a elementului din vrful stivei. Dac presupunem c stiva a a a poate memora maxim 100 de elemente, ambele operatii pot provoca exceptii. Pentru a personaliza aceste exceptii vom crea o clas specic denumit a a a ExceptieStiva: Listing 3.3: Exceptii proprii
class ExceptieStiva extends Exception { public ExceptieStiva ( String mesaj ) { super ( mesaj ) ; } } class Stiva { int elemente [] = new int [100]; int n =0; // numarul de elemente din stiva public void adauga ( int x ) throws ExceptieStiva { if ( n ==100) throw new ExceptieStiva ( " Stiva este plina ! " ) ; elemente [ n ++] = x ; } public int scoate () throws ExceptieStiva { if ( n ==0) throw new ExceptieStiva ( " Stiva este goala ! " ) ; return elemente [n - -]; } }

Secventa cheie este extends Exception care specic faptul c noua a a clas ExceptieStiva este subclas a clasei Exception i deci implementeaz a a s a obiecte ce reprezint exceptii. a

94

CAPITOLUL 3. EXCEPTII

In general, codul adugat claselor pentru exceptii proprii este nesemnicativ: a unul sau doi constructori care aeaza un mesaj de eroare la ieirea standard. s s Procesul de creare a unei noi exceptii poate dus mai departe prin adugarea a unor noi metode clasei ce descrie acea exceptie, a aceasta dezvoltare nu ns si are rostul majoritatea cazurilor. Exceptiile proprii sunt descrise uzual n de clase foarte simple, chiar fr nici un cod ele, cum ar : aa n class ExceptieSimpla extends Exception { } Aceast clas se bazeaz pe constructorul implicit creat de compilator a a a a nu are constructorul ExceptieSimpla(String s). ns

Capitolul 4 Intrri i ieiri a s s


4.1
4.1.1

Introducere
Ce sunt uxurile?

Majoritatea aplicatiilor necesit citirea unor informatii care se gsesc pe a a o surs extern sau trimiterea unor informatii ctre o destinatie extern. a a a a Informatia se poate gsi oriunde: a ntr-un ier pe disc, retea, memorie s n n sau alt program i poate de orice tip: date primitive, obiecte, imagini, n s sunete, etc. Pentru a aduce informatii dintr-un mediu extern, un progam Java trebuie s deschid un canal de comunicatie (ux) de la sursa informatiilor a a (ier, memorie, socket, etc) i s citeasc secvential informatiile respective. s s a a Similar, un program poate trimite informatii ctre o destinatie extern a a deschiznd un canal de comunicatie (ux) ctre acea destinatie i scriind a a s secvential informatiile respective. Indiferent de tipul informatiilor, citirea/scrierea de pe/ctre un mediu a extern respect urmtorul algoritm: a a deschide canal comunicatie while (mai sunt informatii) { citeste/scrie informatie; } inchide canal comunicatie; Pentru a generaliza, att sursa extern a unor date ct i destinatia lor a a a s sunt vzute ca ind nite procese care produc, respectiv consum informatii. a s a 95

96

CAPITOLUL 4. INTRARI SI IESIRI

Denitii: Un ux este un canal de comunicatie unidirectional ntre dou procese. a Un proces care descrie o surs extern de date se numete proces productor. a a s a Un proces care descrie o destinatie extern pentru date se numete proces a s consumator. Un ux care citete date se numete ux de intrare. s s Un ux care scrie date se numete ux de ieire. s s

Observatii: Fluxurile sunt canale de comunicatie seriale pe 8 sau 16 biti. Fluxurile sunt unidirectionale, de la productor la consumator. a Fiecare ux are un singur proces productor i un singur proces consumator. a s Intre dou procese pot exista oricte uxuri, orice proces putnd att proa a a a ducator ct i consumator acelai timp, dar pe uxuri diferite. a s n s Consumatorul i producatorul nu comunic direct printr-o interfat de ux s a a ci prin intermediul codului Java de tratare a uxurilor.

Clasele i intefetele standard pentru lucrul cu uxuri se gsesc pachetul s a n java.io. Deci, orice program care necesit operatii de intrare sau ieire trea s buie s contin instructiunea de import a pachetului java.io: a a import java.io.*;

4.1.2

Clasicarea uxurilor

Exist trei tipuri de clasicare a uxurilor: a Dup directia canalului de comunicatie deschis uxurile se a mpart n: uxuri de intrare (pentru citirea datelor) uxuri de ieire (pentru scrierea datelor) s Dup tipul de date pe care opereaz: a a uxuri de octeti (comunicarea serial se realizeaz pe 8 biti) a a uxuri de caractere (comunicarea serial se realizeaz pe 16 biti) a a

4.1. INTRODUCERE Dup actiunea lor: a

97

uxuri primare de citire/scriere a datelor (se ocup efectiv cu a citirea/scrierea datelor) uxuri pentru procesarea datelor

4.1.3

Ierarhia claselor pentru lucrul cu uxuri

Clasele rdcin pentru ierarhiile ce reprezint uxuri de caractere sunt: a a a a Reader- pentru uxuri de intrare i s Writer- pentru uxuri de ieire. s Acestea sunt superclase abstracte pentru toate clasele ce implementeaz a uxuri specializate pentru citirea/scrierea datelor pe 16 biti i vor contine s metodele comune tuturor. Ca o regul general, toate clasele din aceste ierarhii vor avea terminatia a a Reader sau Writer functie de tipul lor, cum ar exemplele: FileReader, n n BufferedReader, FileWriter, BufferedWriter, etc. De asemenea, se observ ca o alt regul general, faptul c unui ux de intrare XReader a a a a a i corespunde uzual un ux de ieire XWriter, a acest lucru nu este obligas ns toriu.

Clasele radacin pentru ierarhia uxurilor de octeti sunt: a InputStream- pentru uxuri de intrare i s OutputStream- pentru uxuri de ieire. s Acestea sunt superclase abstracte pentru clase ce implementeaz uxuri a specializate pentru citirea/scrierea datelor pe 8 biti. Ca i cazul ux s n urilor pe caractere denumirile claselor vor avea terminatia superclasei lor: FileInputStream, BufferedInputStream, FileOutputStream, BufferedOutputStream, etc., ecrui ux de intrare XInputStream corea spunzndu-i uzual un ux de ieire XOutputStream, fr ca acest lucru s a s aa a e obligatoriu.

98

CAPITOLUL 4. INTRARI SI IESIRI

Pn la un punct, exist un paralelism a a a ntre ierarhia claselor pentru uxuri de caractere i cea pentru uxurile pe octeti. Pentru majoritatea pros gramelor este recomandat ca scrierea i citirea datelor s se fac prin inters a a mediul uxurilor de caractere, deoarece acestea permit manipularea caracterelor Unicode timp ce uxurile de octeti permit doar lucrul pe 8 biti n caractere ASCII.

4.1.4

Metode comune uxurilor

Superclasele abstracte Reader i InputStream denesc metode similare pens tru citirea datelor. Reader InputStream int read() int read() int read(char buf[]) int read(byte buf[]) ... ... De asemenea, ambele clase pun la dispozitie metode pentru marcarea unei locatii ntr-un ux, saltul peste un numr de pozitii, resetarea pozitiei a curente, etc. Acestea sunt a mai rar folosite i nu vor detaliate. ns s Superclasele abstracte Writer i OutputStream sunt de asemenea paralele, s denind metode similare pentru scrierea datelor: Reader void write(int c) void write(char buf[]) void write(String str) ... InputStream void write(int c) void write(byte buf[]) ...

Inchiderea oricrui ux se realizeaz prin metoda close. In cazul care a a n aceasta nu este apelat explicit, uxul va automat a nchis de ctre colectorul a de gunoaie atunci cnd nu va mai exista nici o referint la el, a acest lucru a a ns trebuie evitat deoarece, la lucrul cu uxrui cu zon tampon de memorie, a datele din memorie vor pierdute la nchiderea uxului de ctre gc. a Metodele referitoare la uxuri pot genera exceptii de tipul IOException sau derivate din aceast clas, tratarea lor ind obligatorie. a a

4.2. FOLOSIREA FLUXURILOR

99

4.2

Folosirea uxurilor

Aa cum am vzut, uxurile pot artite functie de activitatea lor s a mp n uxuri care se ocup efectiv cu citirea/scrierea datelor i uxuri pentru n a s procesarea datelor (de ltrare). In continuare, vom vedea care sunt cele mai importante clase din cele dou categorii i la ce folosesc acestea, precum i a s s modalitile de creare i utilizare a uxurilor. at s

4.2.1

Fluxuri primitive

Fluxurile primitive sunt responsabile cu citirea/scrierea efectiv a datelor, a punnd la dispozitie implementri ale metodelor de baz read, respectiv a a a write, denite superclase. In functie de tipul sursei datelor, ele pot n artite astfel: mp Fiier s FileReader, FileWriter FileInputStream, FileOutputStream Numite i uxuri ier, acestea sunt folosite pentru citirea datelor s s dintr-un ier, respectiv scrierea datelor s ntr-un ier i vor analizate s s ntr-o sectiune separat (vezi Fluxuri pentru lucrul cu iere). a s Memorie CharArrayReader, CharArrayWriter ByteArrayInputStream, ByteArrayOutputStream Aceste uxuri folosesc pentru scrierea/citirea informatiilor n/din memorie i sunt create pe un vector existent deja. Cu alte cuvinte, permit s tratarea vectorilor ca surs/destinatie pentru crearea unor uxuri de a intrare/ieire. s StringReader, StringWriter Permit tratarea irurilor de caractere aate memorie ca surs/destinatie s n a pentru crearea de uxuri. Pipe PipedReader, PipedWriter PipedInputStream, PipedOutputStream Implementeaz componentele de intrare/ieire ale unei conducte de a s

100

CAPITOLUL 4. INTRARI SI IESIRI date (pipe). Pipe-urile sunt folosite pentru a canaliza ieirea unui pros gram sau r de executie ctre intrarea altui program sau r de executie. a

4.2.2

Fluxuri de procesare

Fluxurile de procesare (sau de ltrare) sunt responsabile cu preluarea datelor de la un ux primitiv i procesarea acestora pentru a le oferi s ntr-o alt form, a a mai util dintr-un anumit punct de vedere. De exemplu, BufferedReader a poate prelua date de la un ux FileReader i s ofere informatia dintr-un s a ier linie cu linie. Fiind primitiv, FileReader nu putea citi dect caracter s a cu caracter. Un ux de procesare nu poate folosit dect a mpreun cu un a ux primitiv. Clasele ce descriu aceste uxuri pot mpartite functie de tipul de n procesare pe care efectueaza astfel: l Buerizare BufferedReader, BufferedWriter BufferedInputStream, BufferedOutputStream Sunt folosite pentru a introduce un buer procesul de citire/scriere n a informatiilor, reducnd astfel numrul de accesri la dispozitivul ce a a a reprezint sursa/destinatia original a datelor. Sunt mult mai eciente a a dect uxurile fr buer i din acest motiv se recomand folosirea lor a aa s a ori de cte ori este posibil (vezi Citirea i scrierea cu zona tampon). a s Filtrare FilterReader, FilterWriter FilterInputStream, FilterOutputStream Sunt clase abstracte ce denesc o interfat comun pentru uxuri care a a ltreaz automat datele citite sau scrise (vezi Fluxuri pentru ltrare). a Conversie octeti-caractere InputStreamReader, OutputStreamWriter Formeaz o punte de legatur a a ntre uxurile de caractere i uxurile s de octeti. Un ux InputStreamReader citete octeti dintr-un ux s InputStream i convertete la caractere, folosind codicarea stans i s dard a caracterelor sau o codicare specicat de program. Similar, a un ux OutputStreamWriter convertete caractere octeti i trimite s n s rezutatul ctre un ux de tipul OutputStream. a

4.2. FOLOSIREA FLUXURILOR

101

Concatenare SequenceInputStream Concateneaz mai multe uxuri de intrare a ntr-unul singur (vezi Concatenarea ierelor). s Serializare ObjectInputStream, ObjectOutputStream Sunt folosite pentru serializarea obiectelor (vezi Serializarea obiectelor). Conversie tipuri de date DataInputStream, DataOutputStream Folosite la scrierea/citirea datelor de tip primitiv ntr-un format binar, independent de maina pe care se lucreaz (vezi Folosirea claselor s a DataInputStream i DataOutputStream). s Numrare a LineNumberReader LineNumberInputStream Ofer i posibilitatea de numrare automat a liniilor citite de la un as a a ux de intrare. Citire avans n PushbackReader PushbackInputStream Sunt uxuri de intrare care au un buer de 1-caracter(octet) care n este citit avans i caracterul (octetul) care urmeaz celui curent citit. n s a Aare s PrintWriter PrintStream Ofer metode convenabile pentru asarea informatiilor. a

4.2.3

Crearea unui ux

Orice ux este un obiect al clasei ce implementeaz uxul respectiv. Crearea a unui ux se realizeaz aadar similar cu crearea obiectelor, prin instructiunea a s new i invocarea unui constructor corespunztor al clasei respective: s a Exemple:

102

CAPITOLUL 4. INTRARI SI IESIRI

//crearea unui flux de intrare pe caractere FileReader in = new FileReader("fisier.txt"); //crearea unui flux de iesire pe caractere FileWriter out = new FileWriter("fisier.txt"); //crearea unui flux de intrare pe octeti FileInputStream in = new FileInputStream("fisier.dat"); //crearea unui flux de iesire pe octeti FileOutputStrem out = new FileOutputStream("fisier.dat"); Aadar, crearea unui ux primitiv de date care citete/scrie informatii de s s la un dispozitiv extern are formatul general: FluxPrimitiv numeFlux = new FluxPrimitiv(dispozitivExtern); Fluxurile de procesare nu pot exista de sine stttoare ci se suprapun pe aa un ux primitiv de citire/scriere a datelor. Din acest motiv, constructorii claselor pentru uxurile de procesare nu primesc ca argument un dispozitiv extern de memorare a datelor ci o referinta la un ux primitiv responsabil cu citirea/scrierea efectiv a datelor: a Exemple: //crearea unui flux de intrare printr-un buffer BufferedReader in = new BufferedReader( new FileReader("fisier.txt")); //echivalent cu FileReader fr = new FileReader("fisier.txt"); BufferedReader in = new BufferedReader(fr); //crearea unui flux de iesire printr-un buffer BufferedWriter out = new BufferedWriter( new FileWriter("fisier.txt"))); //echivalent cu FileWriter fo = new FileWriter("fisier.txt"); BufferedWriter out = new BufferedWriter(fo); Aadar, crearea unui ux pentru procesarea datelor are formatul general: s

4.2. FOLOSIREA FLUXURILOR

103

FluxProcesare numeFlux = new FluxProcesare(uxPrimitiv); In general, uxurile pot compuse succesiuni orict de lungi: n a DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("fisier.dat")));

4.2.4

Fluxuri pentru lucrul cu iere s

Fluxurile pentru lucrul cu iere sunt cele mai usor de s nteles, ntruct a operatia lor de baz este citirea, respectiv scrierea unui caracter sau octet a dintr-un sau ntr-un ier specicat uzual prin numele su complet sau relativ s a la directorul curent. Dup cum am vzut deja, clasele care implementeaz aceste uxuri sunt a a a urmtoarele: a FileReader, FileWriter - caractere FileInputStream, FileOutputStream - octeti Constructorii acestor clase accept ca argument un obiect care s specice a a un anume ier. Acesta poate un ir de caractere, on obiect de tip File s s sau un obiect de tip FileDesciptor (vezi Clasa File). Constructorii clasei FileReader sunt: public FileReader(String fileName) throws FileNotFoundException public FileReader(File file) throws FileNotFoundException public FileReader(FileDescriptor fd) Constructorii clasei FileWriter: public FileWriter(String fileName) throws IOException public FileWriter(File file) throws IOException public FileWriter(FileDescriptor fd) public FileWriter(String fileName, boolean throws IOException

append)

104

CAPITOLUL 4. INTRARI SI IESIRI

Cei mai uzuali constructori sunt cei care primesc ca argument numele ierului. Acetia pot provoca exceptii de tipul FileNotFoundException s s n cazul care ierul cu numele specicat nu exist. Din acest motiv orice n s a creare a unui ux de acest tip trebuie fcut a a ntr-un bloc try-catch sau metoda care sunt create uxurile respective trebuie s arunce exceptiile n a de tipul FileNotFoundException sau de tipul superclasei IOException.

S considerm ca exemplu un program care copie continutul unui ier a a s cu numele in.txt ntr-un alt ier cu numele out.txt. Ambele iere sunt s s considerate directorul curent. n Listing 4.1: Copierea unui sier
import java . io .*; public class Copiere { public static void main ( String [] args )

try { FileReader in = new FileReader ( " in . txt " ) ; FileWriter out = new FileWriter ( " out . txt " ) ; int c ; while (( c = in . read () ) != -1) out . write ( c ) ; in . close () ; out . close () ; } catch ( IOException e ) { System . err . println ( " Eroare la operatiile cu fisiere ! " ) ; e . printStackTrace () ; } } }

In cazul care vom lansa aplicatia iar directorul curent nu exist un n n a ier cu numele in.txt, va generat o exceptie de tipul s a FileNotFoundException. Aceasta va prins de program deoarece, IOException a este superclas pentru FileNotFoundException. a Dac exist ierul in.txt, aplicatia va crea un nou ier out.txt care a a s s n va copiat continutul primului. Dac exist deja ierul out.txt el va re a a s

4.2. FOLOSIREA FLUXURILOR

105

scris. Dac doream s facem operatia de adugare(append) i nu de rescriere a a a s pentru ierul out.txt foloseam: s FileWriter out = new FileWriter("out.txt", true);

4.2.5

Citirea i scrierea cu buer s

Clasele pentru citirea/scrierea cu zona tampon sunt: BufferedReader, BufferedWriter - caractere BufferedInputStream, BufferedOutputStream - octeti Sunt folosite pentru a introduce un buer (zon de memorie) procea n sul de citire/scriere a informatiilor, reducnd astfel numarul de accesri ale a a dispozitivului ce reprezint sursa/destinatia atelor. Din acest motiv, sunt a mult mai eciente dect uxurile fr buer i din acest motiv se recomand a aa s a folosirea lor ori de cte ori este posibil. a Clasa BufferedReader citete avans date i le memoreaz s n s a ntr-o zon a tampon. Atunci cnd se execut o operatie de citire, caracterul va prea a luat din buer. In cazul care buer-ul este gol, citirea se face direct din n ux i, odat cu citirea caracterului, vor memorati buer i caracterele s a n s care urmeaz. Evident, BufferedInputStream functioneaz dup acelai i a a a s principiu, singura diferent ind faptul c sunt cititi octeti. a a Similar lucreaza i clasele BufferedWriter i BufferedOutputStream. s s La operatiile de scriere datele scrise nu vor ajunge direct la destinatie, ci vor memorate jntr-un buer de o anumit dimensiune. Atunci cnd buerul a a este plin, continutul acestuia va transferat automat la destinatie. Fluxurile de citire/scriere cu buer sunt uxuri de procesare i sunt s folosite prin suprapunere cu alte uxuri, dintre care obligatoriu unul este primitiv. BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream("out.dat"), 1024) //1024 este dimensiunea bufferului Constructorii cei mai folositi ai acestor clase sunt urmtorii: a BufferedReader(Reader in) BufferedReader(Reader in, int dim_buffer) BufferedWriter(Writer out)

106

CAPITOLUL 4. INTRARI SI IESIRI

BufferedWriter(Writer out, int dim_buffer) BufferedInputStream(InputStream in) BufferedInputStream(InputStream in, int dim_buffer) BufferedOutputStream(OutputStream out) BufferedOutputStream(OutputStream out, int dim_buffer) In cazul constructorilor care dimensiunea buer-ului nu este specicat, n a aceasta primete valoarea implicit de 512 octeti (caractere). s a Metodele acestor clase sunt cele uzuale de tipul read i write. Pe lnga s a acestea, clasele pentru scriere prin buer mai au i metoda flush care golete s s explicit zona tampon, chiar dac aceasta nu este plin. a a BufferedWriter out = new BufferedWriter( new FileWriter("out.dat"), 1024) //am creat un flux cu buffer de 1024 octeti for(int i=0; i<1000; i++) out.write(i); //bufferul nu este plin, in fisier nu s-a scris nimic out.flush(); //bufferul este golit, datele se scriu in fisier

Metoda readLine Este specic uxurilor de citire cu buer i permite citirea linie cu linie a a s datelor de intrare. O linie reprezint o succesiune de caractere terminat cu a a simbolul pentru sfrit de linie, dependent de platforma de lucru. Acesta as este reprezentat Java prin secventa escape \n; n BufferedReader br = new BufferedReader(new FileReader("in")) String linie; while ((linie = br.readLine()) != null) { ... //proceseaza linie } br.close(); }

4.2. FOLOSIREA FLUXURILOR

107

4.2.6

Concatenarea uxurilor

Clasa SequenceInputStream permite unei aplicatii s combine serial mai a multe uxuri de intrare astfel at acestea s apar ca un singur ux de nc a a intrare. Citirea datelor dintr-un astfel de ux se face astfel: se citete din s primul ux de intrare specicat pna cnd se ajunge la sfrsitul acestuia, a a a dup care primul ux de intrare este a nchis i se deschide automat urmtorul s a ux de intrare din care se vor citi continuare datele, dup care procesul se n a repet pna la terminarea tuturor uxurilor de intrare. a a Constructorii acestei clase sunt: SequenceInputStream(Enumeration e) SequenceInputStream(InputStream s1, InputStream s2) Primul construieste un ux secvential dintr-o multime de uxuri de in trare. Fiecare obiect enumerarea primit ca parametru trebuie s e de n a a tipul InputStream. Cel de-al doilea construiete un ux de intrare care combin doar dou uxuri s a a s1 i s2, primul ux citit ind s1. s Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea a dou sau mai multor iere: a s Listing 4.2: Concatenarea a dou iere a s
/* Concatenarea a doua fisiere ale caror nume sunt primite de la linia de comanda . Rezultatul concatenarii este afisat pe ecran . */ import java . io .*; public class Concatenare { public static void main ( String args []) { if ( args . length <= 1) { System . out . println ( " Argumente insuficiente ! " ) ; System . exit ( -1) ; } try { FileInputStream f1 = new FileInputStream ( args [0]) ; FileInputStream f2 = new FileInputStream ( args [1]) ; SequenceInputStream s = new S eq u e nc e I np u tS t r ea m ( f1 , f2 ) ; int c ; while (( c = s . read () ) != -1) System . out . print (( char ) c ) ;

108

CAPITOLUL 4. INTRARI SI IESIRI


s . close () ; // f1 si f2 sunt inchise automat } catch ( IOException e ) { e . printStackTrace () ; }

} }

Pentru concatenarea mai multor iere exist dou variante: s a a folosirea unei enumerri - primul constructor (vezi Colectii); a concatenarea pe rnd a acestora folosind al 2-lea constructor; cona catenarea a 3 iere va construi un ux de intrare astfel: s FileInputStream f1 = new FileInputStream(args[0]); FileInputStream f2 = new FileInputStream(args[1]); FileInputStream f3 = new FileInputStream(args[2]); SequenceInputStream s = new SequenceInputStream( f1, new SequenceInputStream(f2, f3));

4.2.7

Fluxuri pentru ltrarea datelor

Un ux de ltrare se ataeaz altui ux pentru a ltra datele care sunt s a citite/scrise de ctre acel ux. Clasele pentru ltrarea datelor superclasele a abstracte: FilterInputStream - pentru ltrarea uxurilor de intrare i s FilterOutputStream - pentru ltrarea uxurilor de ieire. s Cele mai importante uxruri pentru ltrarea datelor sunt implementate de clasele: DataInputStream, DataOutputStream BufferedInputStream, BufferedOutputStream LineNumberInputStream PushbackInputStream PrintStream

4.2. FOLOSIREA FLUXURILOR Observati c toate aceste clase descriu uxuri de octeti. a

109

Filtrarea datelor nu trebuie vzut ca o metod de a elimina anumiti a a a octeti dintr-un ux ci de a transforma aceti octeti date care s poat s n a a interpretate sub alt form. Aa cum am vzut la citirea/scrierea cu zon a a s a a tampon, clasele de ltrare BufferedInputStream i BufferedOutputStream s colecteaz datele unui ux a ntr-un buer, urmnd ca citirea/scrierea s se a a fac prin intermediu acelui buer. a Aadar, uxurile de ltrare nu elimin date citite sau scrise de un anumit s a ux, ci introduc o noua modalitate de manipulare a lor, ele mai ind numite i uxuri de procesare. Din acest motiv, uxurile de ltrare vor contine s anumite metode specializate pentru citirea/scrierea datelor, altele dect cele a comune tuturor uxurilor. De exemplu, clasa BufferedInputStream pune la dispozitie metoda readLine pentru citirea unei linii din uxul de intrare. Folosirea uxurilor de ltrare se face prin ataarea lor de un ux care se s ocup efectiv de citirea/scrierea datelor: a FluxFiltrare numeFlux = new FluxFiltrare(referintaAltFlux);

4.2.8

Clasele DataInputStream i DataOutputStream s

Aceste clase ofer metode prin care un ux nu mai este vzut ca o a a nsiruire de octeti, ci de date primitive. Prin urmare, vor furniza metode pentru citirea i scrierea datelor la nivel de tip primitiv i nu la nivel de octet. s s Clasele care ofer un astfel de suport implementeaz interfetele DataInput, a a respectiv DataOutput. Acestea denesc metodele pe care trebuie s le pun la a a dispozitie vederea citireii/scrierii datelor de tip primitiv. Cele mai folosite n metode, altele dect cele comune tuturor uxurilor, sunt date tabelul de a n mai jos:

110 DataInputStream readBoolean readByte readChar readDouble readFloat readInt readLong readShort readUTF

CAPITOLUL 4. INTRARI SI IESIRI DataOutputStream writeBoolean writeByte writeChar writeDouble writeFloat writeInt writeLong writeShort writeUTF

Aceste metode au denumirile generice de readXXX i writeXXX, specis cate de interfetele DataInput i DataOutput i pot provoca exceptii de tipul s s IOException. Denumirile lor sunt sugestive pentru tipul de date pe care l prelucreaz. mai putin readUTF i writeUTF care se ocup cu obiecte de tip a s a String, ind singurul tip referint permis de aceste clase. a Scrierea datelor folosind uxuri de acest tip se face format binar, ceea n ce nseamn c un ier care au fost scrise informatii folosind metode a a s n writeXXX nu va putea citit dect prin metode readXXX. a Transformarea unei valori format binar se numete serializare. Clasele n s DataInputStream i DataOutputStream permit serializarea tipurilor prims itive i a irurilor de caractere. Serializarea celorlalte tipuri referint va s s a fcut prin intermediul altor clase, cum ar ObjectInputStream i a a s ObjectOutputStream (vezi Serializarea obiectelor).

4.3

Intrri i ieiri formatate a s s

Incepnd cu versiunea 1.5, limbajul Java pune la dispozitii modaliti sima at plicate pentru aarea formatat a unor informatii, respectiv pentru citirea s a de date formatate de la tastatur. a

4.3.1

Intrri formatate a

Clasa java.util.Scanner ofer o solutie simpl pentru formatarea unor informatii a a citite de pe un ux de intrare e pe octeti, e pe caractere, sau chiar dintr-un obiect de tip File. Pentru a citi de la tastatur vom specica ca argument a al constructorului uxul System.in:

4.4. FLUXURI STANDARD DE INTRARE SI IESIRE Scanner s = Scanner.create(System.in); String nume = s.next(); int varsta = s.nextInt(); double salariu = s.nextDouble(); s.close();

111

4.3.2

Ieiri formatate s

Clasele PrintStream i PrintWriter pun la dispozitiile, pe lng metodele s a a print, println care ofereau posibilitatea de a aa un ir de caractere, i s s s metodele format, printf (echivalente) ce permit aarea formatat a unor s a variabile. System.out.printf("%s %8.2f %2d %n", nume, salariu, varsta); Formatarea irurilor de caractere se bazeaz pe clasa java.util.Formatter. s a

4.4

Fluxuri standard de intrare i ieire s s

Mergnd pe linia introdus de sistemul de operare UNIX, orice program Java a a are : o intrare standard o ieire standard s o ieire standard pentru erori s In general, intrarea standard este tastatura iar ieirea standard este ecranul. s Intrarea i ieirea standard sunt reprezentate de obiecte pre-create ce s s descriu uxuri de date care comunic cu dispozitivele standard ale sistemului. a Aceste obiecte sunt denite publice clasa System i sunt: n s System.in - uxul standar de intrare, de tip InputStream System.out - uxul standar de ieire, de tip PrintStream s System.err - uxul standar pentru erori, de tip PrintStream

112

CAPITOLUL 4. INTRARI SI IESIRI

4.4.1

Asarea informatiilor pe ecran

Am vzut deja numeroase exemple de utilizare a uxului standard de ieire, a s el ind folosit la aarea oricror rezultate pe ecran ( modul consola): s a n System.out.print (argument); System.out.println(argument); System.out.printf (format, argumente...); System.out.format (format, argumente...); Fluxul standard pentru aarea erorilor se folosete similar i apare uzual s s s secventele de tratare a exceptiilor. Implicit, este acelai cu uxul standard n s de ieire. s catch(Exception e) { System.err.println("Exceptie:" + e); } Fluxurile de ieire pot folosite aadar fr probleme deoarece tipul lor s s aa este PrintStream, clas concret pentru scrierea datelor. In schimb, uxul a a standard de intrare System.out este de tip InputStream, care este o clas a abstract, deci pentru a-l putea utiliza ecient va trebui sa-l folosim a mpreuna cu un ux de procesare(ltrare) care s permit citirea facil a datelor. a a a

4.4.2

Citirea datelor de la tastatur a

Uzual, vom dori s folosim metoda readLine pentru citirea datelor de la a tastatura i din acest motiv vom folosi intrarea standard s mpreun cu o clas a a de procesare care ofer aceast metod. Exemplul tipic este: a a a BufferedReader stdin = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Introduceti o linie:"); String linie = stdin.readLine() System.out.println(linie); In exemplul urmtor este prezentat un program care aeaza liniile introa s duse de la tastatur pn momentul care se introduce linia exit sau a a a n n o linie vid i mentioneazdac irul respectiv reprezint un numr sau nu. as a as a a

4.4. FLUXURI STANDARD DE INTRARE SI IESIRE Listing 4.3: Citirea datelor de la tastatur a
/* Citeste siruri de la tastatura si verifica daca reprezinta numere sau nu */ import java . io .*; public class EsteNumar { public static void main ( String [] args ) { BufferedReader stdin = new BufferedReader ( new InputStreamReader ( System . in ) ) ; try { while ( true ) { String s = stdin . readLine () ; if ( s . equals ( " exit " ) || s . length () ==0) break ; System . out . print ( s ) ; try { Double . parseDouble ( s ) ; System . out . println ( " : DA " ) ; } catch ( Numb erFor ma t E x c e p t i o n e ) { System . out . println ( " : NU " ) ; } } } catch ( IOException e ) { System . err . println ( " Eroare la intrarea standard ! " ) ; e . printStackTrace () ; } } }

113

Incepnd cu versiunea 1.5, varianta cea mai comod de citire a datelor a a de la tastatur este folosirea clasei java.util.Scanner. a

4.4.3

Redirectarea uxurilor standard

Redirectarea uxurilor standard presupune stabilirea unei alte surse dect a tastatura pentru citirea datelor, respectiv alte destinatii dect ecranul pentru a cele dou uxuri de ieire. In clasa System exist urmtoarele metode statice a s a a care realizeaz acest lucru: a setIn(InputStream) - redirectare intrare setOut(PrintStream) - redirectare iesire setErr(PrintStream) - redirectare erori

114

CAPITOLUL 4. INTRARI SI IESIRI

Redirectarea ieirii este util special atunci cnd sunt aate foarte multe s a n a s date pe ecran. Putem redirecta asarea ctre un ier pe care s-l citim dup a s a a executia programului. Secventa clasic de redirectare a ieirii este ctre un a s a ier este: s PrintStream fis = new PrintStream( new FileOutputStream("rezultate.txt"))); System.setOut(fis); Redirectarea erorilor ntr-un ier poate de asemenea util i se face s as ntr-o manier similar: a a PrintStream fis = new PrintStream( new FileOutputStream("erori.txt"))); System.setErr(fis); Redirectarea intrrii poate folositoare pentru un program mod cona n sol care primete mai multe valori de intrare. Pentru a nu le scrie de la a s tastatur de ecare dat timpul testrii programului, ele pot puse a a n a ntrun ier, redirectnd intrarea standard ctre acel ier. In momentul cnd s a a s a testarea programului a luat sfrsit redirectarea poate eliminat, datele ind a a cerute din nou de la tastatur. a Listing 4.4: Exemplu de folosire a redirectrii: a
import java . io .*; class Redirectare { public static void main ( String [] args ) { try { BufferedInputS t r ea m in = new B uf f e re d I np u tS t r ea m ( new FileInputStream ( " intrare . txt " ) ) ; PrintStream out = new PrintStream ( new FileOutputStream ( " rezultate . txt " ) ) ; PrintStream err = new PrintStream ( new FileOutputStream ( " erori . txt " ) ) ; System . setIn ( in ) ; System . setOut ( out ) ; System . setErr ( err ) ; BufferedReader br = new BufferedReader ( new InputStream Reader ( System . in ) ) ;

4.4. FLUXURI STANDARD DE INTRARE SI IESIRE


String s ; while (( s = br . readLine () ) != null ) { /* Liniile vor fi citite din fisierul intrare . txt si vor fi scrise in fisierul rezultate . txt */ System . out . println ( s ) ; } // Aruncam fortat o exceptie throw new IOException ( " Test " ) ; } catch ( IOException e ) { /* Daca apar exceptii , ele vor fi scrise in fisierul erori . txt */ System . err . println ( " Eroare intrare / iesire ! " ) ; e . printStackTrace () ; } } }

115

4.4.4

Analiza lexical pe uxuri (clasa StreamTokenizer) a

Clasa StreamTokenizer proceseaz un ux de intrare de orice tip i a s l mparte atomi lexicali. Rezultatul va consta faptul c loc s se citeasc n n a n a a octeti sau caractere, se vor citi, pe rnd, atomii lexicali ai uxului respectiv. a Printr-un atom lexical se n]elege general: n un identicator (un ir care nu este s ntre ghilimele) un numar un ir de caractere s un comentariu un separator Atomii lexicali sunt desprtiti a ntre ei de separatori. Implicit, aceti separas tori sunt cei obinuti: spatiu, tab, virgul, punct i virgula, etc., a pot s a s ns schimbati prin diverse metode ale clasei. Constructorii clasei sunt:

116

CAPITOLUL 4. INTRARI SI IESIRI

public StreamTokenizer(Reader r) public StreamTokenizer(InputStream is) Identicarea tipului i valorii unui atom lexical se face prin intermediul s variabilelor: TT EOF - atom ce marcheaz sfaritul uxului a a s TT EOL - atom ce marcheaz sfritul unei linii a as TT NUMBER - atom de tip numr a TT WORD- atom de tip cuvnt a ttype- tipul ultimului atom citit din ux nval- valoarea unui atom numeric sval - valoarea unui atom de tip cuvnt a Citirea atomilor din ux se face cu metoda nextToken(), care returnez a tipul atomului lexical citit i scrie variabilele nval sau sval valoarea cores n spunzatore atomului. a Exemplul tipic de folosire a unui analizor lexical este citirea unei secvente de numere i iruri aate s s ntr-un ier sau primite de la tastatur: s a Listing 4.5: Citirea unor atomi lexicali dintr-un sier
/* Citirea unei secvente de numere si siruri dintr - un fisier specificat si afisarea tipului si valorii lor */ import java . io .*; public class CitireAtomi { public static void main ( String args []) throws IOException { BufferedReader br = new BufferedReader ( new FileReader ( " fisier . txt " ) ) ; StreamTokenizer st = new StreamTokenizer ( br ) ; int tip = st . nextToken () ; // Se citeste primul atom lexical

4.5. CLASA RANDOMACCESFILE (FISIERE CU ACCES DIRECT)


while ( tip != StreamTokenizer . TT_EOF ) { switch ( tip ) { case StreamTokenizer . TT_WORD : System . out . println ( " Cuvant : " + st . sval ) ; break ; case StreamTokenizer . TT_NUMBER : System . out . println ( " Numar : " + st . nval ) ; } tip = st . nextToken () ; // Trecem la urmatorul atom } } }

117

Aadar, modul de utilizare tipic pentru un analizor lexical este s ntr-o bucla while, care se citesc atomii unul cte unul cu metoda nextToken, n a pna se ajunge la sfrsitul uxului (TT EOF). In cadrul buclei while se detera a min tipul atomul curent curent ( a ntors de metoda nextToken) i apoi se a s a valoarea numeric sau irul de caractere corespunztor atomului respectiv. a s a In cazul care tipul atomilor nu ne intereseaz, este mai simplu s n a a citim uxul linie cu linie i s folosim clasa StringTokenizer, care realizeaz s a a artirea unui ir de caractere atomi lexicali, sau metoda split a clasei mp s n String.

4.5

Clasa RandomAccesFile (iere cu acces dis rect)

Dup cum am vzut, uxurile sunt procese secventiale de intrare/ieire. a a s Acestea sunt adecvate pentru lucrul cu medii secventiale de memorare a datelor, cum ar banda magnetic sau pentru transmiterea informatiilor a prin retea, desi sunt foarte utile i pentru dispozitive care informatia poate s n accesat direct. a Clasa RandomAccesFile are urmtoarele caracteristici: a permite accesul nesecvential (direct) la continutul unui ier; s este o clas de sine stttoare, subclas direct a clasei Object; a aa a a se gsete pachetul java.io; a s n

118

CAPITOLUL 4. INTRARI SI IESIRI

implementeaz interfetele DataInput i DataOutput, ceea ce a s nseamna ca sunt disponibile metode de tipul readXXX, writeXXX, ntocmai ca la clasele DataInputStream i DataOutputStream; s permite att citirea ct i scriere din/in iere cu acces direct; a a s s permite specicarea modului de acces al unui ier (read-only, reads write). Constructorii acestei clase sunt: RandomAccessFile(StringnumeFisier, StringmodAcces) throws IOException RandomAccessFile(StringnumeFisier, StringmodAcces) throws IOException unde modAcces poate : r - ierul este deschis numai pentru citire (read-only) s rw - ierul este deschis pentru citire i scriere (read-write) s s Exemple: RandomAccesFile f1 = new RandomAccessFile("fisier.txt", "r"); //deschide un fisier pentru citire RandomAccesFile f2 = new RandomAccessFile("fisier.txt", "rw"); //deschide un fisier pentru scriere si citire Clasa RandomAccesFile suport notiunea de pointer de ier. Acesta este a s un indicator ce specic pozitia curent ier. La deschiderea unui ier a a n s s pointerul are valoarea 0, indicnd a nceputul ierului. Apeluri la metodele s de citire/scrirere deplaseaz pointerul ierului cu numrul de octeti cititi a s a sau scrii de metodele respective. s In plus fatde metodele de tip read i write clasa pune la dispozitie i a s s metode pentru controlul pozitiei pointerului de ier. Acestea sunt: s skipBytes - mut pointerul ierului a s nainte cu un numr specicat de a octeti seek - pozitioneaza pointerului ierului s naintea unui octet specicat getFilePointer - returneaz pozitia pointerului de ier. a s

4.6. CLASA FILE

119

4.6

Clasa File

Clasa File nu se refer doar la un ier ci poate reprezenta e un ier a s s anume, e multimea ierelor dintr-un director. s Specicarea unui ier/director se face prin specicarea cii absolute spre s a acel ier sau a cii relative fat de directorul curent. Acestea trebuie s res a a a specte conventiile de specicare a cilor i numelor ierelor de pe platforma a s s de lucru. Utilitate clasei File const furnizarea unei modaliti de a abstractiza a n at dependentele cailor i numelor ierelor fatde maina gazd, precum i s s a s a s punerea la dispozitie a unor metode pentru lucrul cu sere i directoare la s nivelul sistemului de operare. Astfel, aceast clas vom gsi metode pentru testarea existentei, tergerea, n a a a s redenumirea unui ier sau director, crearea unui director, listarea ierelor s s dintr-un director, etc. Trebuie mentionat i faptul c majoritatea constructorilor uxurilor care s a permit accesul la iere accept ca argument un obiect de tip File locul s a n unui ir ce reprezint numele ierului respectiv. s a s File f = new File("fisier.txt"); FileInputStream in = new FileInputStream(f) Cel mai uzual constructor al clasei File este: public File(String numeFisier) Metodele mai importante ale clasei File au denumiri sugestive i vor s prezentate prin intermediul exemplului urmtor care listeaz ierele i suba a s s directoarele unui director specicat i, pentru ecare din ele aeaz diverse s s a informatii: Listing 4.6: Listarea continutului unui director
/* Programul listeaza fisierele si subdirectoarele unui director . Pentru fiecare din ele vor fi afisate diverse informatii . Numele directorului este primit ca argument de la linia de comanda , sau este directorul curent . */ import java . io .*;

120
import java . util .*; public class ListareDirector {

CAPITOLUL 4. INTRARI SI IESIRI

private static void info ( File f ) { // Afiseaza informatii despre un fisier sau director String nume = f . getName () ; if ( f . isFile () ) System . out . println ( " Fisier : " + nume ) ; else if ( f . isDirectory () ) System . out . println ( " Director : " + nume ) ; System . out . println ( " Cale absoluta : " + f . getAbsolutePath () + " \ n Poate citi : " + f . canRead () + " \ n Poate scrie : " + f . canWrite () + " \ n Parinte : " + f . getParent () + " \ n Cale : " + f . getPath () + " \ n Lungime : " + f . length () + " \ n Data ultimei modificari : " + new Date ( f . lastModified () ) ) ; System . out . println ( " - - - - - - - - - - - - - - " ) ; } public static void main ( String [] args ) { String nume ; if ( args . length == 0) nume = " . " ; // directorul curent else nume = args [0]; try { File director = new File ( nume ) ; File [] continut = director . listFiles () ; for ( int i = 0; i < continut . length ; i ++) info ( continut [ i ]) ; } catch ( Exception e ) { e . printStackTrace () ; } } }

Capitolul 5 Interfete
5.1
5.1.1

Introducere
Ce este o interfat ? a

Interfetele duc conceptul de clas abstract cu un pas a a nainte prin eliminarea oricror implementri de metode, punnd practic unul din conceptele a a a n a programrii orientate obiect i anume cel de separare a modelului unui obiect a s (interfat) de implementarea sa. Aadar, o interfat poate privita ca un a s a protocol de comunicare ntre obiecte. O interfat Java denete un set de metode dar nu specic nici o implea s a mentare pentru ele. O clas care implementeaz o interfat trebuie obligatoa a a riu s specice implementri pentru toate metodele interfetei, supunndu-se a a a aadar unui anumit comportament. s Denitie O interfat este o colectie de metode fr implementare i declaratii de a aa s constante. Interfetele permit, alturi de clase, denirea unor noi tipuri de date. a 121

122

CAPITOLUL 5. INTERFETE

5.2
5.2.1

Folosirea interfetelor
Denirea unei interfete

Denirea unei interfete se face prin intermediul cuvntului cheie interface: a [public] interface NumeInterfata [extends SuperInterfata1, SuperInterfata2...] { /* Corpul interfetei: Declaratii de constane Declaratii de metode abstracte */ } O interfat poate avea un singur modicator i anume public. O interfat a s a public este accesibil tuturor claselor, indiferent de pachetul din care fac a a parte, implicit nivelul de acces ind doar la nivelul pachetului din care face parte interfata. O interfat poate extinde oricte interfete. Acestea se numesc superinterfete a a i sunt separate prin virgul. (vezi Motenirea multipl prin intermediul s a s a interfetelor). Corpul unei interfete poate contine: constante: acestea pot sau nu declarate cu modicatorii public, static i final care sunt impliciti, nici un alt modicator neputnd s a aprea declaratia unei variabile dintr-o interfat. Constantele unei a n a interfete trebuie obligatoriu initializate. interface Exemplu { int MAX = 100; // Echivalent cu: public static final int MAX = 100; int MAX; // Incorect, lipseste initializarea private int x = 1; // Incorect, modificator nepermis }

5.2. FOLOSIREA INTERFETELOR

123

metode fr implementare: acestea pot sau nu declarate cu moda a icatorul public, care este implicit; nici un alt modicator nu poate aprea declaratia unei metode a unei interfete. a n interface Exemplu { void metoda(); // Echivalent cu: public void metoda(); protected void metoda2(); // Incorect, modificator nepermis

Atentie Variabilele unei interfete sunt implicit publice chiar dac nu sunt declarate a cu modicatorul public. Variabilele unei interfete sunt implicit constante chiar dac nu sunt a declarate cu modicatorii static i final. s Metodele unei interfete sunt implicit publice chiar dac nu sunt declarate a cu modicatorul public. In variantele mai vechi de Java era permis i modicatorul abstract s declaratia interfetei i declaratiile metodelor, a acest lucru nu n s n ns mai este valabil, deoarece att interfata ct i metodele sale nu pot a a s altfel dect abstracte. a

5.2.2

Implementarea unei interfete

Implementarea uneia sau mai multor interfete de ctre o clas se face prin a a intermediul cuvntului cheie implements: a class NumeClasa implements NumeInterfata sau class NumeClasa implements Interfata1, Interfata2, ...

124

CAPITOLUL 5. INTERFETE

O clas poate implementa oricte interfete sau poate s nu implementeze a a a nici una. In cazul care o clas implementeaz o anumit interfat, atunci tren a a a a buie obligatoriu s specice cod pentru toate metodele interfetei. Din acest a motiv, odat creata i folosit la implementarea unor clase, o interfat nu a s a a mai trebuie modicat, sensul c adugarea unor metode noi sau schima n a a barea signaturii metodelor existente vor duce la erori compilarea claselor n care o implementeaz. Evident, o clas poate avea i alte metode i variabile a a s s membre afar de cele denite interfat. n a n a

Atentie Modicarea unei interfete implic modicarea tuturor claselor care im a plementeaz acea interfat. a a

O interfat nu este o clas, dar orice referint de tip interfat poate primi a a a a ca valoare o referinta la un obiect al unei clase ce implementeaz interfata a respectiv. Din acest motiv, interfetele pot privite ca tipuri de date i vom a s spune adesea c un obiect are tipul X, unde X este o interfat, dac acesta a a a este o instant a unei clase ce implementeaz interfata X. a a Implementarea unei interfete poate s e i o clas abstract. a s a a

5.2.3

Exemplu: implementarea unei stive

S considerm urmtorul exemplu. Dorim s implementm un nou tip de a a a a a date numit Stack, care s modeleze notiunea de stiv de obiecte. Obiectele a a de tip stiv, indiferent de implementarea lor, vor trebui s contin metodele: a a a push - adaug un nou element in stv a a pop - elimin elementul din vrful stivei a a peek - returneaz varful stivei a empty - testeaz dac stiva este vid a a a toString - returneaz continutul stivei sub forma unui ir de caractere. a s

5.2. FOLOSIREA INTERFETELOR

125

Din punctul de vedere al structurii interne, o stiv poate implementat a a folosind un vector sau o list antuit, ambele solutii avnd avantaje i a nl a a s dezavantaje. Prima solutie este mai simplu de eles, timp ce a doua este nt n mai ecient din punctul de vedere al folosirii memoriei. Deoarece nu dorim a s legm tipul de date Stack de o anumit implementare structural, vom a a a a l deni prin intermediul unei interfete. Vom vedea imediat avantajele acestei abordri. a Listing 5.1: Interfata ce descrie stiva
public interface Stack { void push ( Object item ) throws StackException ; void pop () throws StackException ; Object peek () throws StackException ; boolean empty () ; String toString () ; }

Pentru a trata situatiile anormale care pot aprea atunci cnd a a ncercm a s punem un element stiv i nu este posibil din lips de memorie, sau a n a s a ncercm s accesm vrful stivei i aceasta este vid, vom deni o exceptie a a a a s a proprie StackException: Listing 5.2: Tipul de exceptie generat de stiv a
public class StackException extends Exception { public StackException () { super () ; } public StackException ( String msg ) { super ( msg ) ; } }

Dm continuare prima implementare a stivei, folosind un vector: a n Listing 5.3: Implementarea stivei folosind un vector
// Implementarea stivei folosind un vector de obiecte . public class StackImpl1 implements Stack { private Object items []; // Vectorul ce contine obiectele

126

CAPITOLUL 5. INTERFETE

private int n =0; // Numarul curent de elemente din stiva public StackImpl1 ( int max ) { // Constructor items = new Object [ max ]; } public StackImpl1 () { this (100) ; } public void push ( Object item ) throws StackException { if ( n == items . length ) throw new StackException ( " Stiva este plina ! " ) ; items [ n ++] = item ; } public void pop () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; items [ - - n ] = null ; } public Object peek () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; return items [n -1]; } public boolean empty () { return ( n ==0) ; } public String toString () { String s = " " ; for ( int i =n -1; i >=0; i - -) s += items [ i ]. toString () + " " ; return s ; } }

Remarcati c, dei interfat metodele nu sunt declarate explicit cu a s n a modicatorul public, ele sunt totui publice i trebuie declarate ca atare s s n clas. a Trebuie remarcat i faptul c metoda toString este denit deja clasa s a a n Object, deci clasa noastr o are deja implementat i nu am obtinut nici a as o eroare la compilare dac nu o implementam explicit. Ceea ce facem acum a este de fapt supradenirea ei.

5.2. FOLOSIREA INTERFETELOR

127

O alt observatie important se refer la faptul c trebuie s declarm a a a a a a n cadrul interfetei i exceptiile aruncate de metode, ce trebuie obligatoriu s tratate. S vedem acum modalitatea de implementare a stivei folosind o list a a antuit: nl a Listing 5.4: Implementarea stivei folosind o list a
// Implementarea stivei folosind o lista inlantuita . public class StackImpl2 implements Stack { class Node { // Clasa interna ce reprezinta un nod al listei Object item ; // informatia din nod Node link ; // legatura la urmatorul nod Node ( Object item , Node link ) { this . item = item ; this . link = link ; } } private Node top = null ; // Referinta la varful stivei public void push ( Object item ) { Node node = new Node ( item , top ) ; top = node ; } public void pop () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; top = top . link ; } public Object peek () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; return top . item ; } public boolean empty () { return ( top == null ) ; } public String toString () { String s = " " ; Node node = top ; while ( node != null ) {

128

CAPITOLUL 5. INTERFETE
s += ( node . item ) . toString () + " " ; node = node . link ; } return s ; }

Singura observatie pe care o facem aici este c, dei metoda push din a s interfat declar aruncarea unor exceptii de tipul StackException, nu este a a obligatoriu ca metoda din clas s specice i ea acest lucru, att timp ct a a s a a nu genereaz exceptii de acel tip. Invers este a obligatoriu. a ns In continuare este prezentat o mic aplicatie demonstrativ care folosete a a a s tipul de date nou creat i cele dou implementri ale sale: s a a Listing 5.5: Folosirea stivei
public class TestStiva { public static void afiseaza ( Stack s ) { System . out . println ( " Continutul stivei este : " + s ) ; } public static void main ( String args []) { try { Stack s1 = new StackImpl1 () ; s1 . push ( " a " ) ; s1 . push ( " b " ) ; afiseaza ( s1 ) ; Stack s2 = new StackImpl2 () ; s2 . push ( new Integer (1) ) ; s2 . push ( new Double (3.14) ) ; afiseaza ( s2 ) ;

} catch ( StackException e ) { System . err . println ( " Eroare la lucrul cu stiva ! " ) ; e . printStackTrace () ; } } }

Observati folosirea interfetei Stack ca un tip de date, ce aduce exibilitate sporit manevrarea claselor ce implementeaz tipul respectiv. Metoda a n a

5.3. INTERFETE SI CLASE ABSTRACTE

129

afiseaza accept ca argument orice obiect al unei clase ce implementeaz a a Stack. Observatie In pachetul java.util exist clasa Stack care modeleaz notiune de stiv a a a de obiecte i, evident, aceasta va folosit aplicatiile ce au nevoie de acest s a n tip de date. Exemplu oferit de noi nu are legtur cu aceast clas i are rol a a a as pur demonstrativ.

5.3

Interfete i clase abstracte s

La prima vedere o interfat nu este altceva dect o clas abstract care a a a a n toate metodele sunt abstracte (nu au nici o implementare). Aadar, o clas abstract nu ar putea s a a nlocui o interfat ? a Raspunsul la intrebare depinde de situatie, a general este Nu. ns n Deosebirea const faptul c unele clase sunt fortate s extind o anumit a n a a a a clas (de exemplu orice applet trebuie s e subclasa a clasei Applet) i nu ar a a s mai putea sa extind o alt clas, deoarece Java nu exista dect motenire a a a n a s simpla. Fara folosirea interfetelor nu am putea forta clasa respectiv s a a respecte diverse tipuri de protocoale. La nivel conceptual, diferenta const a n: extinderea unei clase abstracte forteaz o relatie a ntre clase; implementarea unei interfete specic doar necesitatea implementrii a a unor anumie metode. In multe situatii interfetele i clasele abstracte sunt folosite s mpreun a pentru a implementa ct mai exibil i ecient o anumit ierarhie de clase. Un a s a exemplu sugestiv este dat de clasele ce descriu colectii. Ca sa particularizm, a exist: a interfata List care impune protocolul pe care trebuie s respecte o a l clas de tip list, a a clasa abstract AbstractList care implementeaz interfata List i a a s ofer implementri concrete pentru metodele comune tuturor tipurilor a a de list, a

130

CAPITOLUL 5. INTERFETE

clase concrete, cum ar LinkedList, ArrayList care extind AbstractList.

5.4

Motenire multipl prin interfete s a

Interfetele nu au nici o implementare i nu pot instantiate. Din acest motiv, s nu reprezint nici o problem ca anumite clase s implementeze mai multe a a a interfete sau ca o interfat s extind mai multe interfete (s aib mai multe a a a a a superinterfete) class NumeClasa implements Interfata1, Interfata2, ... interface NumeInterfata extends Interfata1, Interfata2, ... O interfat mosteneste att constantele ct i declaratiile de metode de la a a a s superinterfetele sale. O clas moteneste doar constantele unei interfete i a s s responsabilitatea implementrii metodelor sale. a S considerm un exemplu de clasa care implementeaza mai multe interfete: a a interface Inotator { void inoata(); } interface Zburator { void zboara(); } interface Luptator { void lupta(); } class Erou implements Inotator, Zburator, Luptator { public void inoata() {} public void zboara() {} public void lupta() {} } Exemplu de interfat care extinde mai multe interfete: a interface Monstru { void ameninta(); } interface MonstruPericulos extends Monstru { void distruge();

5.4. MOSTENIRE MULTIPLA PRIN INTERFETE } interface Mortal { void omoara(); } interface Vampir extends void beaSange(); } class Dracula implements public void ameninta() public void distruge() public void omoara()() public void beaSange() }

131

MonstruPericulos, Mortal {

Vampir { {} {} {} {}

Evident, pot aprea situatii de ambiguitate, atunci cnd exist constante a a a sau metode cu aceleai nume mai multe interfete, a acest lucru trebuie s n ns ntotdeauna evitat, deoarece scrierea unui cod care poate confuz este un stil prost de programare. In cazul in care acest lucru se ampl, compilatorul nt a nu va furniza eroare dect dac se a a ncearc referirea constantelor ambigue a fr a le prexa cu numele interfetei sau dac metodele cu acelai nume nu aa a s pot deosbite, cum ar situatia cnd au aceeai list de argumente dar a s a tipuri returnate incompatibile. interface I1 { int x=1; void metoda(); } interface I2 { int x=2; void metoda(); //int metoda(); }

//corect //incorect

class C implements I1, I2 { public void metoda() { System.out.println(I1.x); //corect System.out.println(I2.x); //corect System.out.println(x); //ambiguitate

132 } }

CAPITOLUL 5. INTERFETE

S recapitulm cteva lucruri legate de clase i interfete: a a a s O clas nu poate avea dect o superclas. a a a O clas poate implementa oricte interfete. a a O clas trebuie obligatoriu s trateze metodele din interfetele pe care a a la implementeaz. a Ierarhia interfetelor este independent de ierarhia claselor care le im a plementeaz. a

5.5

Utilitatea interfetelor

Dup cum am vzut, o interfat denete un protocol ce poate implementat a a a s de orice clas, indiferent de ierarhia de clase din care face parte. Interfetele a sunt utile pentru: denirea unor similaritati ntre clase independente fr a forta articial aa o legatur a ntre ele; asigur c toate clasele care implementeaz o interfat pun la dipozitie a a a a metodele specicate interfat - de aici rezult posibilitatea implen a a mentrii unor clase prin mai multe modaliti i folosirea lor a at s ntr-o manier unitar; a a denirea unor grupuri de constante; transmiterea metodelor ca parametri;

5.5.1

Crearea grupurilor de constante

Deoarece orice variabil a unei interfete este implicit declarat cu public, a a static si final, interfetele reprezint o metod convenabil de creare a unor a a a grupuri de constante care s e folosite global a ntr-o aplicatie:

5.5. UTILITATEA INTERFETELOR public interface Luni { int IAN=1, FEB=2, ..., DEC=12; } Folosirea acestor constante se face prin expresii de genul NumeInterfata.constanta, ca exemplul de mai jos: n if (luna < Luni.DEC) luna ++ else luna = Luni.IAN;

133

5.5.2

Transmiterea metodelor ca parametri

Deoarece nu exist pointeri propriu-zii, transmiterea metodelor ca parametri a s este realizat Java prin intermediul interfetelor. Atunci cnd o metod a n a a trebuie s primeasc ca argument de intrare o referint la o alt functie a a a a necesar executiei sale, cunoscut doar la momentul executiei, atunci argua a mentul respectiv va declarat de tipul unei interfete care contine metoda respectiv. La executie metoda va putea primi ca parametru orice obiect ce a implementeaz interfata respectiv i deci contine i codul functiei respective, a as s aceasta urmnd s e apelat normal pentru a obtine rezultatul dorit. a a a Aceast tehnic, denumit i call-back, este extrem de folosit Java a a a s a n i trebuie neaprat eleas. S considerm mai multe exemple pentru a s a nt a a a clarica lucrurile. Primul exemplu se refer la explorarea nodurilor unui graf. In ecare a nod trebuie s se execute prelucrarea informatiei din nodul respectiv prin a intermediul unei functii primite ca parametru. Pentru aceasta, vom deni o interfata Functie care va specica metoda trimis ca parametru. a

interface Functie { public void executa(Nod u); } class Graf { //... void explorare(Functie f) { //...

134

CAPITOLUL 5. INTERFETE if (explorarea a ajuns in nodul v) { f.executa(v); //... }

} } //Definim doua functii class AfisareRo implements Functie { public void executa(Nod v) { System.out.println("Nodul curent este: " + v); } } class AfisareEn implements Functie { public void executa(Nod v) { System.out.println("Current node is: " + v); } } public class TestCallBack { public static void main(String args[]) { Graf G = new Graf(); G.explorare(new AfisareRo()); G.explorare(new AfisareEn()); } } Al doilea xemplu va prezentat sectiunea urmtoare, n a ntruct face a parte din API-ul standard Java i vor puse evident, prin intermediul s n a su, i alte tehnici de programare. a s

5.6

Interfata FilenameFilter

Instantele claselor ce implementeaz aceasta interfat sunt folosite pentru a a a crea ltre pentru iere i sunt primite ca argumente de metode care listeaz s s a continutul unui director, cum ar metoda list a clasei File. Aadar, putem spune c metoda list primete ca argument o alt functie s a s a care specic dac un ier va returnat sau nu (criteriul de ltrare). a a s

5.6. INTERFATA FILENAMEFILTER

135

Interfata FilenameFilter are o singur metod: accept care specic a a a criteriul de ltrare i anume, testeaz dac numele ierului primit ca paras a a s metru ndeplinete conditiile dorite de noi. s Denitia interfetei este: public interface FilenameFilter { public boolean accept(File dir, String numeFisier); } Aadar, orice clas de specicare a unui ltru care implementez interfata s a a FilenameFilter trebuie s implementeze metoda accept a acestei interfete. a Aceste clase mai pot avea i alte metode, de exemplu un constructor care s s a primeasca criteriul de ltrare. In general, o clas de specicare a unui ltru a are urmtorul format: a class FiltruFisiere implements FilenameFilter { String filtru; // Constructorul FiltruFisiere(String filtru) { this.filtru = filtru; } // Implementarea metodei accept public boolean accept(File dir, String nume) { if (filtrul este indeplinit) return true; else return false; } } Metodele cele mai uzuale ale clasei String folosite pentru ltrarea ierelor s sunt: endsWith - testeaz dac un ir are o anumit terminatie a a s a indexOf - testeaz dac un ir contine un anumit subir, returnnd a a s s a pozitia acestuia, sau 0 caz contrar. n

136

CAPITOLUL 5. INTERFETE

Instantele claselor pentru ltrare sunt primite ca argumente de metode de listare a continutului unui director. O astfel de metod este list din a clasa File: String[] list (FilenameFilter filtru) Observati c aici interfata este folosit ca un tip de date, ea ind substia a tuit cu orice clas care o implementeaz. Acesta este un exemplu tipic de a a a transmitere a unei functii (functia de ltrare accept) ca argument al unei metode. S considerm exemplul complet care dorim s listm ierele din dia a n a a s rectorul curent care au o anumit extensie. a Listing 5.6: Listarea ierelor cu o anumit extensie s a
/* Listarea fisierelor din directorul curent care au anumita extensie primita ca argument . Daca nu se primeste nici un argument , vor fi listate toate . */ import java . io .*; class Listare { public static void main ( String [] args ) { try { File director = new File ( " . " ) ; String [] list ; if ( args . length > 0) list = director . list ( new Filtru ( args [0]) ) ; else list = director . list () ; for ( int i = 0; i < list . length ; i ++) System . out . println ( list [ i ]) ; } catch ( Exception e ) { e . printStackTrace () ; } } } class Filtru implements FilenameFilter { String extensie ; Filtru ( String extensie ) { this . extensie = extensie ;

5.6. INTERFATA FILENAMEFILTER


} public boolean accept ( File dir , String nume ) { return ( nume . endsWith ( " . " + extensie ) ) ; } }

137

5.6.1

Folosirea claselor anonime

In cazul care nu avem nevoie de clasa care reprezint ltrul pentru listarea n a ierelor dintr-un director dect o singur dat, pentru a evita crearea unei s a a a noi clase de sine stttoare care s e folosit pentru instantierea unui singur aa a a obiect, putem folosi clas intern anonim, aceast situatie ind un exemplu a a a a tipic de folosire a acestora. Listing 5.7: Folosirea unei clase anonime
/* Listarea fisierelor din directorul curent folosind o clasa anonima pentru filtru . */ import java . io .*; class Listare { public static void main ( String [] args ) { try { File director = new File ( " . " ) ; String [] list ; if ( args . length > 0) { final String extensie = args [0]; list = director . list ( new FilenameFilter () { // Clasa interna anonima public boolean accept ( File dir , String nume ) { return ( nume . endsWith ( " . " + extensie ) ) ; } }) ; } else list = director . list () ; for ( int i = 0; i < list . length ; i ++) System . out . println ( list [ i ]) ; } catch ( Exception e ) { e . printStackTrace () ; }

138
} }

CAPITOLUL 5. INTERFETE

Aadar, o modalitate uzual de folosire a claselor anonime pentru instantierea s a unui obiect care trebuie s respecte o interfat este: a a metoda(new Interfata() { // Implementarea metodelor interfetei });

5.7

Compararea obiectelor

Am vzut primul capitol c o solutie facil i ecient de sortare a unui a n a as a vector este folosirea metodei sort din clasa java.util.Arrays. int v[]={3, 1, 4, 2}; java.util.Arrays.sort(v); // Sorteaza vectorul v // Acesta va deveni {1, 2, 3, 4} In cazul care elementele din vector sunt de tip primitiv, ca in exemn plul de mai sus, nu exist nici o problem a determina ordinea reasc a a n a a elementelor. Ce se ampl a atunci cnd vectorul contine referinte la nt a ns a obiecte de un anumit tip ? S considerm urmtorul exemplu, care dorim a a a n s sortm un vector format din instante ale clasei Persoana, denit mai jos: a a a Listing 5.8: Clasa Persoana (fr suport pentru comparare) aa
class Persoana { int cod ; String nume ; public Persoana ( int cod , String nume ) { this . cod = cod ; this . nume = nume ; } public String toString () { return cod + " \ t " + nume ; } }

5.7. COMPARAREA OBIECTELOR Programul urmtor ar trebui s sorteze un vector de persoane: a a Listing 5.9: Sortarea unui vector de tip referint a
class Sortare { public static void main ( String args []) { Persoana p [] = new Persoana [4]; p [0] = new Persoana (3 , " Ionescu " ) ; p [1] = new Persoana (1 , " Vasilescu " ) ; p [2] = new Persoana (2 , " Georgescu " ) ; p [3] = new Persoana (4 , " Popescu " ) ; java . util . Arrays . sort ( p ) ; System . out . println ( " Persoanele ordonate dupa cod : " ) ; for ( int i =0; i < p . length ; i ++) System . out . println ( p [ i ]) ; } }

139

La executia acestei aplicatii va obtinut o exceptie, deoarece metoda a sort nu tie care este ordinea natural a obiectelor de tip Persoana. Va s a trebui, ntr-un fel sau altul, s specicm acest lucru. a a

5.7.1

Interfata Comparable

Interfata Comparable impune o ordine total asupra obiectelor unei clase ce a o implementeaz. Aceast ordine se numete ordinea natural a clasei i este a a s a s specicat prin intermediul metodei compareTo. Denitia interfetei este: a public interface Comparable { int compareTo(Object o); } Aadar, o clas ale crei instante trebuie s e comparabil va implementa s a a a metoda compareTo care trebuie s returneze: a o valoare strict negativ: dac obiectul curent (this) este mai mic a a dec obiectul primit ca argument; a zero: dac obiectul curent este egal dec obiectul primit ca argument; a a

140

CAPITOLUL 5. INTERFETE

o valoare strict pozitiv: dac obiectul curent este mai mare dec a a a obiectul primit ca argument. Reamintim c metoda equals, motenit din Object de toate clasele, a s a determin dac dou obiecte sunt egale (au aceeai valoare). Spunem c a a a s a ordinea natural a unei clase C este consitent cu equals dac i numai a a a s dac (e1.compareTo((Object)e2) == 0) are aceeasi valoare logic cu a s a e1.equals((Object)e2, pentru orice e1, e2 instante ale lui C. null nu este instant a nici unei clase i e.compareTo(null) trebuie s a s a arunce o exceptie de tip NullPointerException chiar dac e.equals(null) a returneaz false. a S presupunem c dorim ca ordinea natural a persoanelor s e dup a a a a a codul lor intern. Listing 5.10: Clasa Persoana cu suport pentru comparare
class Persoana implements Comparable { int cod ; String nume ; public Persoana ( int cod , String nume ) { this . cod = cod ; this . nume = nume ; } public String toString () { return cod + " \ t " + nume ; } public boolean equals ( Object o ) { if (!( o instanceof Persoana ) ) return false ; Persoana p = ( Persoana ) o ; return ( cod == p . cod ) && ( nume . equals ( p . nume ) ) ; } public int compareTo ( Object o ) { if ( o == null ) throw new Null P o i n t e r E x c e p t i o n () ; if (!( o instanceof Persoana ) ) throw new Clas sCas tExce ptio n ( " Nu pot compara ! " ) ; Persoana p = ( Persoana ) o ;

5.7. COMPARAREA OBIECTELOR


return ( cod - p . cod ) ; } }

141

Observati folosirea operatorului instanceof, care veric dac un obiect a a este instant a unei anumite clase. Metoda compareTo va arunca o exceptie a de tipul ClassCastException dac se a ncearc compararea unui obiect de a tip Persoana cu un obiect de alt tip. Metoda equals va returna, pur i s simplu, false.

5.7.2

Interfata Comparator

In cazul care dorim s sortm elementele unui vector ce contine referinte n a a dup alt criteriu dect ordinea natural a elemenetelor, avem nevoie de o alt a a a a solutie. Aceasta este oferit tot de metoda sort din clasa java.util.Arrays, a dar varianta care, pe lng vectorul ce trebuie sortat, vom transmite n n a a un argument de tip Comparator care s specice modalitatea de comparare a a elementelor. Interfata java.util.Comparator contine metoda compare, care impune o ordine total asupra elementelor unei colectii. Aceasta returneaz un a a ntreg cu aceeai semnicatie ca la metoda compareTo a interfetei Comparator i s s are urmtoarea denitie: int compare(Object o1, Object o2); a S presupunem c dorim s sortm persoanele ordonate dup numele lor. a a a a a Pentru denirea comparatorului vom folosi o clas anonim. a a Listing 5.11: Sortarea unui vector folosind un comparator
import java . util .*; class Sortare { public static void main ( String args []) { Persoana p [] = new Persoana [4]; p [0] = new Persoana (3 , " Ionescu " ) ; p [1] = new Persoana (1 , " Vasilescu " ) ; p [2] = new Persoana (2 , " Georgescu " ) ; p [3] = new Persoana (4 , " Popescu " ) ; Arrays . sort (p , new Comparator () { public int compare ( Object o1 , Object o2 ) { Persoana p1 = ( Persoana ) o1 ; Persoana p2 = ( Persoana ) o2 ; return ( p1 . nume . compareTo ( p2 . nume ) ) ;

142

CAPITOLUL 5. INTERFETE
} }) ; System . out . println ( " Persoanele ordonate dupa nume : " ) ; for ( int i =0; i < p . length ; i ++) System . out . println ( p [ i ]) ;

} }

Observati cum compararea a dou iruri de caractere se face tot cu metoda as compareTo, clasa String implemennd interfata Comparable. a

5.8

Adaptori

In cazul care o interfat contine mai multe metode i, la un moment n a s dat, avem nevoie de un obiect care implementeaz interfata respectiv dar nu a specic cod dect pentru o singur metod, el trebui totui s implementeze a a a a s a toate metodele interfetei, chiar dac nu specic nici un cod. a a interface X { void metoda_1(); void metoda_2(); ... void metoda_n(); } ... // Avem nevoie de un obiect de tip X // ca argument al unei functii functie(new X() { public void metoda_1() { // Singura metoda care ne intereseaza ... } // Trebuie sa apara si celelalte metode // chiar daca nu au implementare efectiva public void metoda_2() {} public void metoda_3() {} ... public void metoda_n() {}

5.8. ADAPTORI });

143

Aceast abordare poate neplcut dac avem frecvent nevoie de obiecte a a a a ale unor clase ce implementeaz interfata X. Solutia la aceast problem este a a a folosirea adaptorilor. Denitie Un adaptor este o clas abstract care implementeaz o anumit interfat a a a a a fr a specica cod nici unei metode a interfetei. aa

public abstract class XAdapter implements X { public void metoda_1() {} public void metoda_2() {} ... public void metoda_n() {} } In situatia cnd avem nevoie de un obiect de tip X vom folosi clasa a abstract, supradenind doar metoda care ne intereseaz: a a functie(new XAdapter() { public void metoda_1() { // Singura metoda care ne intereseaza ... } }); Mai multe exemple de folosire a adaptorilor vor date capitolul Interfata n grac cu utilizatorul. a

144

CAPITOLUL 5. INTERFETE

Capitolul 6 Organizarea claselor


6.1 Pachete

Denitie Un pachet este o colectie de clase i interfete s nrudite din punctul de vedere al functionalitii lor. Sunt folosite pentru gsirea i utilizarea mai at a s uoar a claselor, pentru a evita conictele de nume i pentru a controla s a s accesul la anumite clase. In alte limbaje de programare pachetele se mai numesc librrii sau bibilioteci. a

6.1.1

Pachetele standard (J2SDK)

Platforma standard de lucru Java se bazeaz pe o serie de pachete cu ajutorul a crora se pot construi a ntr-o manier simplicat aplicatiile. Exist deci un a a a set de clase deja implementate care modeleaz structuri de date, algoritmi sau a diverse notiuni esentiale dezvoltarea unui program. Cele mai importante n pachete i suportul oferit de lor sunt: s java.lang - clasele de baz ale limbajului Java a java.io - intrri/ieiri, lucrul cu iere a s s java.util - clase i interfete utile s java.applet - dezvoltarea de appleturi 145

146

CAPITOLUL 6. ORGANIZAREA CLASELOR

java.awt - interfata grac cu utilizatorul a java.awt.event - mecanismele de tratare e evenimentelor generate de utilizator java.beans - scrierea de componente reutilizabile java.net - programare de retea java.sql - lucrul cu baze de date java.rmi - executie la distant Remote Message Interface a java.security - mecanisme de securitate: criptare, autenticare java.math - operatii matematice cu numere mari java.text - lucrul cu texte, date i numere independent de limb s a java.lang.reect - introspectie javax.swing - interfata grac cu utilizatorul, mult a mbogit fat de at a a AWT. ...

6.1.2

Folosirea membrilor unui pachet

Conform specicatiilor de acces ale unei clase i ale mebrilor ei, doar clasele s publice i membrii declarati publici ai unei clase sunt accesibili afara pas n chetului care se gsesc. Dup cum am vzut sectiunea Specicatori n a a a n de acces pentru membrii unei clase, accesul implicit Java este la nivel de n pachet. Pentru a folosi o clas public dintr-un anumit pachet, sau pentru a apela a a o metod public a unei clase publice a unui pachet, exist trei solutii: a a a specicarea numelui complet al clasei importul clasei respective importul ntregului pachet care se gsete clasa. n a s

6.1. PACHETE

147

Specicarea numelui complet al clasei se face prin prexarea numelui scurt al clasei cu numele pachetului din care face parte: numePachet.NumeClasa. Button java.awt java.awt.Button - numele scurt al clasei - pachetul din care face parte - numele complet al clasei

Aceast metod este recomandat doar pentru cazul care folosirea a a a n acelei clase se face o singur dat sau foarte rar. a a De exemplu, ar extrem de neplcut s scriem de ecare dat cnd vrem a a a a s declarm un obiect grac secvente de genul: a a java.awt.Button b1 java.awt.Button b2 java.awt.TextField java.awt.TextField = new java.awt.Button("OK"); = new java.awt.Button("Cancel"); tf1 = new java.awt.TextField("Neplacut"); tf2 = new java.awt.TextField("Tot neplacut");

In aceste situatii, vom importa aplicatia noastr clasa respectiv, sau n a a ntreg pachetul din care face parte. Acest lucru se realizeaz prin instructiunea a import, care trebuie s apar la a a nceputul ierelor surs, s a nainte de declararea vreunei clase sau interfete.

6.1.3

Importul unei clase sau interfete

Se face prin instructiunea import care specicm numele complet al clasei n a sau interfetei pe care dorim s o folosim dintr-un anumit pacehet: a import numePachet.numeClasa; //Pentru exemplul nostru: import java.awt.Button; import java.awt.TextField; Din acest moment, vom putea folosi clasele ierului care am plasat n s n instructiunea de import numele scurt al claselor Button i TextField: s Button b1 Button b2 TextField TextField = new Button("OK"); = new Button("Cancel"); tf1 = new TextField("Placut"); tf2 = new TextField("Foarte placut");

148

CAPITOLUL 6. ORGANIZAREA CLASELOR

Aceast abordare este ecient i recomandat cazul care nu avem a as a n n nevoie dect de cteva clase din pachetul respectiv. Dac exemplul nostru a a a n am avea nevoie i de clasele Line, Point, Rectangle, Polygon, ar trebui s s a avem cte o instructiune de import pentru ecare dintre ele: a import import import import import import java.awt.Button; java.awt.TextField; java.awt.Rectangle; java.awt.Line; java.awt.Point; java.awt.Polygon;

In aceast situatie ar mai simplu s folosim importul la cerere din a a ntregul pachet i nu al ecrei clase parte. s a n

6.1.4

Importul la cerere dintr-un pachet

Importul la cerere dintr-un anumit pachet se face printr-o instructiune import care specicm numele pachetului ale crui clase i interfete dorim s le n a a s a folosim, urmat de simbolul *. Se numete import la cerere deoarece arcarea s nc claselor se face dinamic, momentul apelrii lor. n a import numePachet.*; //Pentru exemplul nostru: import java.awt.*; Din acest moment, vom putea folosi clasele ierului care am plasat n s n instructiunea de import numele scurt al tuturor claselor pachetului importat: Button b = new Button("OK"); Point p = new Point(0, 0);

Atentie * nu are semnicatia uzual de la iere de wildcard (masc) i nu poate a s a s folosit dect ca atare. O expresie de genul import java.awt.C*; va produce a o eroare de compilare.

6.1. PACHETE

149

In cazul care sunt importate dou sau mai multe pachete care contin n a clase (interfete) cu acelai nume, atunci referirea la ele trebuie fcut doar s a a folosind numele complet, caz contrar ind semnalat o ambiguitate de n a ctre compilator. a import java.awt.*; // Contine clasa List import java.util.*; // Contine interfata List ... List x;

//Declaratie ambigua

java.awt.List a = new java.awt.List(); //corect java.util.List b = new ArrayList(); //corect Sunt considerate importate automat, pentru orice ier surs, urmtoarele s a a pachete: pachetul java.lang import java.lang.*; // Poate sau nu sa apara // Mai bine nu... pachetul curent pachetul implicit (fr nume) aa

6.1.5

Importul static

Aceast facilitate, introdus a a ncepnd cu versiunea 1.5, permite referirea cona stantelor statice ale unei clase fr a mai specica numele complet al acesaa teia i este implementat prin adugarea cuvntului cheie static dup cel de s a a a a import: import static numePachet.NumeClasa.*; Astfel, loc s ne referim la constantele clasei cu expresii de tipul n a NumeClasa.CONSTANTA, putem folosi doar numele constantei.

150

CAPITOLUL 6. ORGANIZAREA CLASELOR

// Inainte de versiuna 1.5 import java.awt.BorderLayout.*; ... fereastra.add(new Button(), BorderLayout.CENTER); // Incepand cu versiunea 1.5 import java.awt.BorderLayout.*; import static java.awt.BorderLayout.*; ... fereastra.add(new Button(), CENTER);

Atentie Importul static nu import dect constantele statice ale unei clase, nu i a a s clasa sine. n

6.1.6

Crearea unui pachet

Toate clasele i interfetele Java apartin la diverse pachete, grupate dup s a functionalitatea lor. Dup cum am vzut clasele de baz se gsesc pa a a a a n chetul java.lang, clasele pentru intrri/ieiri sunt java.io, clasele pentru a s n interfata grac java.awt, etc. a n Crearea unui pachet se realizeaz prin scriere la a nceputul ierelor surs s a ce contin clasele i interfetele pe care dorim s le grupm s a a ntr-un pachet a instructiunii: package numePachet; S considerm un exemplu: presupunem c avem dou iere surs Graf.java a a a a s a i Arbore.java. s //Fisierul Graf.java package grafuri; class Graf {...} class GrafPerfect extends Graf {...} //Fisierul Arbore.java package grafuri; class Arbore {...}

6.1. PACHETE class ArboreBinar extends Arbore {...}

151

Clasele Graf, GrafPerfect, Arbore, ArboreBinar vor face parte din acelai pachet grafuri. s Instructiunea package actioneaz asupra a ntregului ier surs la s a nceputul cruia apare. Cu alte cuvinte nu putem specica faptul c anumite clase a a dintr-un ier surs apartin unui pachet, iar altele altui pachet. s a Dac nu este specicat un anumit pachet, clasele unui ier surs vor face a s a parte din pachetul implicit (care nu are nici un nume). In general, pachetul implicit este format din toate clasele i intefetele directorului curent de lucru. s Este recomandat a ca toate clasele i intefetele s e plasate pachete, ns s a n pachetul implicit ind folosit doar pentru aplicatii mici sau prototipuri.

6.1.7

Denumirea unui pachet

Exist posibilitatea ca doi programatori care lucreaz la un proiect comun a a s foloseasc acelai nume pentru unele din clasele lor. De asemenea, se a a s poate ca una din clasele unei aplicatii s aib acelai nume cu o clas a a a s a mediului Java. Acest lucru este posibil att timp ct clasele cu acelai nume a a s se gasesc pachete diferite, ele ind diferentiate prin prexarea lor cu numele n pachetelor. Ce se ampl a cnd doi programatori care lucreaz la un proiect nt a ns a a comun folosesc clase cu acelai nume, ce se gasesc pachete cu acelai nume s n s ? Pentru a evita acest lucru, companiile folosesc inversul domeniului lor Internet denumirea pachetelor implementate cadrul companiei, cum n n ar ro.companie.numePachet. In cadrul aceleiasi companii, conictele de nume vor rezolvate prin diverse conventii de uz intern, cum ar folosirea numelui de cont al programatorilor denumirea pachetelor create de acetia. n s De exemplu, programatorul cu numele Ion al companiei XSoft, avnd contul a ion@xsoft.ro, si va prexa pachetele cu ro.xsoft.ion, pentru a permite identicarea mod unic a claselor sale, indiferent de contextul care acestea n n vor integrate.

152

CAPITOLUL 6. ORGANIZAREA CLASELOR

6.2
6.2.1

Organizarea ierelor s
Organizarea ierelor surs s a

Orice aplicatie nebanal trebuie s e construit folosind o organizare ier a a a arhic a componentelor sale. Este recomandat ca strategia de organizare a a ierelor surs s respecte urmtoarele conventii: s a a a Codul surs al claselor i interfetelor s se gaseasc iere ale cror a s a a n s a nume s e chiar numele lor scurt i care s aib extensia .java. a s a a

Atentie Este obligatoriu ca o clas/interfat public s se gaseasc a a a a a ntr-un ier avnd numele clasei(interfetei) i extenisa .java, sau compilatorul s a s va furniza o eroare. Din acest motiv, ntr-un ier surs nu pot exista s a dou clase sau interfete publice. Pentru clasele care nu sunt publice a acest lucru nu este obligatoriu, ci doar recomandat. Intr-un ier surs s a pot exista oricte clase sau interfete care nu sunt publice. a

Fiierele surs trebuie s se gseasc directoare care s reecte nus a a a a n a mele pachetelor care se gsesc clasele i interfetele din acele iere. n a s s Cu alte cuvinte, un director va contine surse pentru clase i interfete s din acelai pachet iar numele directorului va chiar numele pachetus lui. Dac numele pachetelor sunt formate din mai multe uniti lexicale a at separate prin punct, atunci acestea trebuie de asemenea s corespund a a unor directoare ce vor descrie calea spre ierele surs ale cror clase s a a i interfete fac parte din pachetele respective. s Vom clarica modalitatea de organizare a ierelor surs ale unei aplicatii s a printr-un exemplu concret. S presupunem c dorim crearea unor compoa a nente care s reprezinte diverse notiuni matematice din domenii diferite, a cum ar geometrie, algebr, analiz, etc. Pentru a simplica lucrurile, s a a a presupunem c dorim s crem clase care s descrie urmtoarele notiuni: a a a a a poligon, cerc, poliedru, sfer, grup, functie. a O prim variant ar s construim cte o clas pentru ecare i s le plasm a a a a a s a a

6.2. ORGANIZAREA FISIERELOR

153

acelai director n s mpreuna cu un program care s le foloseasca, a, avnd a ns a vedere posibila extindere a aplicatiei cu noi reprezentri de notiuni matemn a atice, aceast abordare ar inecient. a a O abordare elegant ar aceea care clasele care descriu notiuni din a n acelai domeniu sa se gaseasca pachete separate i directoare separate. s n s Ierarhia ierelor sursa ar : s /matematica /surse /geometrie /plan Poligon.java Cerc.java /spatiu Poliedru.java Sfera.java /algebra Grup.java /analiza Functie.java Matematica.java Clasele descrise ierele de mai sus trebuie declarate pachete denun s n mite corespunzator cu numele directoarelor care se gasesc: n // Poligon.java package geometrie.plan; public class Poligon { . . . } // Cerc.java package geometrie.plan; public class Cerc { . . . }

// Poliedru.java package geometrie.spatiu; public class Poliedru { . . . }

154

CAPITOLUL 6. ORGANIZAREA CLASELOR

// Sfera.java package geometrie.spatiu; public class Sfera { . . . } // Grup.java package algebra; public class Grup { . . . } // Functie.java package analiza; public class Functie { . . . }

Matematica.java este clasa principal a aplicatiei. a Dup cum se observ, numele lung al unei clase trebuie s descrie calea spre a a a acea clas cadrul ierelor surs, relativ la directorul care se gsete a n s a n a s aplicatia.

6.2.2

Organizarea unitilor de compilare (.class) at

In urma compilrii ierelor surs vor generate uniti de compilare pentru a s a at ecare clas i interfat din ierele surs. Dup cum tim acestea au extensia as a s a a s .class i numele scurt al clasei sau interfetei respective. s Spre deosebire de organizarea surselor, un ier .class trebuie s se s a gaseasca ntr-o ierarhie de directoare care s reecte numele pachetului din a care face parte clasa respectiv. a Implicit, urma compilrii ierele surs i unitile de compilare se n a s a s at gsesc acelai director, a ele pot apoi organizate separat. Este recoa n s ns mandat a ca aceast separare s e fcut automat la compilare. ns a a a a Revenind la exemplul de mai sus, vom avea urmtoarea organizare: a /matematica /clase /geometrie /plan Poligon.class Cerc.class /spatiu

6.2. ORGANIZAREA FISIERELOR Poliedru.class Sfera.class /algebra Grup.class /analiza Functie.class Matematica.class

155

Crearea acestei structuri ierarhice este facut automat de ctre compilator. a a In directorul aplicatiei (matematica) crem subdirectorul clase i dm coa s a manda: javac -sourcepath surse surse/Matematica.java -d clase sau javac -classpath surse surse/Matematica.java -d clase Optiunea -d specic directorul rdcin al ierarhiei de clase. In lipsa a a a a lui, ecare unitate de compilare va plasat acelai director cu ierul su a n s s a surs. a Deoarece compilm clasa principal a plicatiei, vor compilate cascad a a n a toate clasele referite de aceasta, dar numai acestea. In cazul care dorim s n a compilm explicit toate ierele java dintr-un anumit director, de exemplu a s surse/geometrie/plan, putem folosi expresia: javac surse/geometrie/plan/*.java -d clase

6.2.3

Necesitatea organizrii ierelor a s

Organizarea ierelor surs este necesar deoarece momentul cnd compis a a n a latorul alneste un nume de clas el trebuie s poat identica acea clas, nt a a a a ceea ce nseamna c trebuie s gaseasc erul surs care o contine. a a a s a Similar, unitile de compilare sunt organizate astfel pentru a da posibilat itatea interpretorului s gaseasc i s a a s a ncarce memorie o anumit clas n a a n timpul executiei programului. Ins aceast organizare nu este sucient deoarece specic numai partea a a a a nal din calea ctre ierele .java i .class, de exemplu a a s s /matematica/clase/geometrie/plan/Poligon.class. Pentru aceasta, att a la compilare ct i la interpretare trebuie specicat lista de directoare rdcin a s a a a a

156

CAPITOLUL 6. ORGANIZAREA CLASELOR

care se gsesc ierele aplicatiei. Aceast list se numete cale de cautare n a s a a s (classpath). Denitie O cale de cutare este o list de directoare sau arhive care vor cutate a a n a ierele necesare unei aplicatii. Fiecare director din calea de cautare este s directorul imediat superior structurii de directoare corespunztoare numelor a pachetelor care se gsesc clasele din directorul respectiv, astfel at compin a nc latorul i interpretorul s poat construi calea complet spre clasele aplicatiei. s a a a Implicit, calea de cutare este format doar din directorul curent. a a S considerm clasa principal a aplicatiei Matematica.java: a a a import geometrie.plan.*; import algebra.Grup; import analiza.Functie; public class Matematica { public static void main(String args[]) { Poligon a = new Poligon(); geometrie.spatiu.Sfera = new geometrie.spatiu.Sfera(); //... } } Identicarea unei clase referite program se face felul urmtor: n n a La directoarele aate calea de cutare se adaug subdirectoarele n a a specicate import sau numele lung al clasei n n In directoarele formate este cutat un ier cu numele clasei. In cazul a s care nu este gsit nici unul sau sunt gsite mai multe va semnalat n a a a o eroare.

6.2.4

Setarea cii de cutare (CLASSPATH) a a

Setarea cii de cutare se poate face dou modaliti: a a n a at Setarea variabilei de mediu CLASSPATH - folosind aceast variant a a toate aplicatiile Java de pe maina respectiv vor cuta clasele necesare s a a directoarele specicate variabila CLASSPATH. n n

6.3. ARHIVE JAR UNIX: SET CLASSPATH = cale1:cale2:... DOS shell (Windows 95/NT/...): SET CLASSPATH = cale1;cale2;...

157

Folosirea optiunii -classpath la compilarea i interpretarea programelor s - directoarele specicate astfel vor valabile doar pentru comanda curent: a javac - classpath <cale de cautare> <surse java> java - classpath <cale de cautare> <clasa principala> Lansarea executie a aplicatiei noastre, din directorul matematica, se n va face astfel: java -classpath clase Matematica In concluzie, o organizare ecient a ierelor aplicatiei ar arta astfel: a s a /matematica /surse /clase compile.bat (javac -sourcepath surse surse/Matematica.java -d clase) run.bat (java -classpath clase Matematica)

6.3

Arhive JAR

Fiierele JAR (Java Archive) sunt arhive format ZIP folosite pentru s n mpachetarea aplicatiilor Java. Ele pot folosite i pentru comprimri s a obinuite, diferenta fat de o arhiv ZIP obinuit ind doar existenta unui s a a s a director denumit META-INF, ce contine diverse informatii auxiliare legate de aplicatia sau clasele arhivate. Un ier JAR poate creat folosind utilitarul jar aat distributia s n J2SDK, sau metode ale claselor suport din pachetul java.util.jar. Dintre beneciile oferite de arhivele JAR amintim: portabilitate - este un format de arhivare independent de platform; a

158

CAPITOLUL 6. ORGANIZAREA CLASELOR

compresare - dimensiunea unei aplicatii forma sa nal este redus; n a a minimizarea timpului de ncarcare a unui applet: dac appletul (iere a s class, resurse, etc) este compresat ntr-o arhiv JAR, el poate arcat a nc ntr-o singur tranzactie HTTP, fr a deci nevoie de a deschide cte a aa a o conexiune nou pentru ecare ier; a s securitate - arhivele JAR pot semnate electronic mecanismul pentru lucrul cu iere JAR este parte integrata a plats formei Java.

6.3.1

Folosirea utilitarului jar

Arhivatorul jar se gsete subdirectorul bin al directorului care este a s n n instalat kitul J2SDK. Mai jos sunt prezentate pe scurt operatiile uzuale: Crearea unei arhive jar cf arhiva.jar fiier(e)-intrare s Vizualizare continutului jar tf nume-arhiva Extragerea continutului jar xf arhiva.jar Extragerea doar a unor iere s jar xf arhiva.jar fiier(e)-arhivate s Executarea unei aplicatii java -jar arhiva.jar Deschiderea unui applet arhivat <applet code=A.class archive="arhiva.jar" ...> Exemple: Arhivarea a dou iere class: a s jar cf classes.jar A.class B.class arhivarea tuturor ierelor din directorul curent: s jar cvf allfiles.jar *

6.3. ARHIVE JAR

159

6.3.2

Executarea aplicatiilor arhivate

Pentru a rula o aplicatie mpachetat a ntr-o arhiv JAR trebuie s facem a a cunoscut interpretorului numele clasei principale a aplicatiei. S considerm a a a urmtorul exemplu, care dorim s arhivm clasele aplicatiei descrise mai a n a a sus, care clasa principal era Matematica.java. Din directorul clase vom n a lansa comanda: jar cvf mate.jar geometrie analiza algebra Matematica.class In urma acestei comenzi vom obtine arhiva mate.jar. Dac vom a ncerca s lansm executie aceast arhiv prin comanda java -jar mate.jar a a n a a vom obtine urmtoarea eroare: Failed to load Main-Class manifest from a mate.jar. Aceasta nseamna c serul Manifest.mf ce se gasete dia n s n rectorul META-INF trebuie s a nregistrm clasa principal a aplicatiei. Acest a a lucru vom face doi pai: l n s se creeaz un ier cu un nume oarecare, de exemplu manifest.txt, a s care vom scrie: n Main-Class: Matematica adaugm aceast informatie la ierul manifest al arhivei mate.jar: a a s jar uvfm mate.jar manifest.txt Ambele operatii puteau executate ntr-un singur pas: jar cvfm mate.jar manifest.txt geometrie analiza algebra Matematica.class Pe sistemele Win32, platforma Java 2 va asocia extensiile .jar cu interpretorul Java, ceea ce nseamn c facnd dublu-click pe o arhiv JAR va a a a a lansat executie aplicatia a n mpachetat acea arhiv (dac exist o clas a n a a a a principal). a

160

CAPITOLUL 6. ORGANIZAREA CLASELOR

Capitolul 7 Colectii
7.1 Introducere

O colectie este un obiect care grupeaz mai multe elemente a ntr-o singur a unitate. Prin intermediul colectiilor vom avea acces la diferite tipuri de date cum ar vectori, liste antuite, stive, multimi matematice, tabele de nl dispersie, etc. Colectiile sunt folosite att pentru memorarea i manipularea a s datelor, ct i pentru transmiterea unor informatii de la o metod la alta. a s a Tipul de date al elementelor dintr-o colectie este Object, ceea ce nseamn a c multimile reprezentate sunt eterogene, putnd include obiecte de orice tip. a a Incepnd cu versiunea 1.2, Java colectiile sunt tratate a n ntr-o manier a unitar, ind organizate a ntr-o arhitectur foarte ecient i exibil ce cuprinde: a as a Interfete: tipuri abstracte de date ce descriu colectiile i permit uti s lizarea lor independent de detaliile implementrilor. a Implementri: implementri concrete ale interfetelor ce descriu colectii. a a Aceste clase reprezint tipuri de date reutilizabile. a Algoritmi: metode care efectueaz diverse operatii utile cum ar a cutarea sau sortarea, denite pentru obiecte ce implementeaz interfetele a a ce descriu colectii. Aceti algoritmi se numesc i polimorci deoarece s s pot folositi pe implementri diferite ale unei colectii, reprezentnd a a elementul de functionalitate reutilizabil. a Utilizarea colectiilor ofer avantaje evidente procesul de dezvoltare a a n unei aplicatii. Cele mai importante sunt: 161

162

CAPITOLUL 7. COLECTII

Reducerea efortului de programare: prin punerea la dispozitia programatorului a unui set de tipuri de date i algoritmi ce modeleaz s a structuri i operatii des folosite aplicatii. s n Creterea vitezei i calitii programului: implementrile efective s s at a ale colectiilor sunt de nalt performant i folosesc algoritmi cu timp a a s de lucru optim. Astfel, la scrierea unei aplicatii putem s ne concentrm eforturile asupra a a problemei sine i nu asupra modului de reprezentare i manipulare a n s s informatiilor.

7.2

Interfete ce descriu colectii

Interfetele reprezint nucleul mecanismului de lucru cu colectii, scopul lor a ind de a permite utilizarea structurilor de date independent de modul lor de implementare.

Collection modeleaz o colectie la nivelul cel mai general, descriind un a grup de obiecte numite i elementele sale. Unele implementri ale acestei s a interfete permit existenta elementelor duplicate, alte implementri nu. Un a ele au elementele ordonate, altele nu. Platforma Java nu ofer nici o ima plementare direct a acestei interfete, ci exist doar implementri ale unor a a a subinterfete mai concrete, cum ar Set sau List. public interface Collection { // Metode cu caracter general int size(); boolean isEmpty(); void clear(); Iterator iterator(); // Operatii la nivel de element boolean contains(Object element); boolean add(Object element); boolean remove(Object element);

7.2. INTERFETE CE DESCRIU COLECTII

163

// Operatii la nivel de multime boolean containsAll(Collection c); boolean addAll(Collection c); boolean removeAll(Collection c); boolean retainAll(Collection c); // Metode de conversie in vector Object[] toArray(); Object[] toArray(Object a[]); }

Set modeleaz notiunea de multime sens matematic. O multime nu a n poate avea elemente duplicate, mai bine zis nu poate contine dou obiecte o1 a i o2 cu proprietatea o1.equals(o2). Motenete metodele din Collection, s s s fr a avea alte metode specice. aa Dou dintre clasele standard care ofer implementri concrete ale acestei a a a interfete sunt HashSet i TreeSet. s

SortedSet este asemntoare cu interfata Set, diferenta principal constnd a a a a faptul c elementele dintr-o astfel de colectie sunt ordonate ascendent. n a Pune la dispozitie operatii care beneciaz de avantajul ordonrii elementelor. a a Ordonarea elementelor se face conform ordinii lor naturale, sau conform cu ordinea dat de un comparator specicat la crearea colectiei i este mentinut a s a automat la orice operatie efectuat asupra multimii. Singura condittie este a ca, pentru orice dou obiecte o1, o2 ale colectiei, apelul o1.compareT o(o2) a (sau comparator.compare(o1, o2), dac este folosit un comparator) trebuie a s e valid i s nu provoace exceptii. a s a Fiind subclas a interfetei Set, motenete metodele acesteia, oferind a s s metode suplimentare ce in cont de faptul c multimea este sortat: t a a public interface SortedSet extends Set { // Subliste SortedSet subSet(Object fromElement, Object toElement); SortedSet headSet(Object toElement);

164

CAPITOLUL 7. COLECTII

SortedSet tailSet(Object fromElement); // Capete Object first(); Object last(); Comparator comparator(); } Clasa care implementeaz aceast interfat este TreeSet. a a a

List descrie liste (secvente) de elemente indexate. Listele pot contine duplicate i permit un control precis asupra pozitiei unui element prin ins termediul indexului acelui element. In plus, fatde metodele denite de a interfata Collection, avem metode pentru acces pozitional, cutare i it a s erare avansat. Denitia interfetei este: a public interface List extends Collection { // Acces pozitional Object get(int index); Object set(int index, Object element); void add(int index, Object element); Object remove(int index); abstract boolean addAll(int index, Collection c); // Cautare int indexOf(Object o); int lastIndexOf(Object o); // Iterare ListIterator listIterator(); ListIterator listIterator(int index); // Extragere sublista List subList(int from, int to); }

7.2. INTERFETE CE DESCRIU COLECTII Clase standard care implementeaz aceast interfat sunt: ArrayList, a a a LinkedList, Vector.

165

Map descrie structuri de date ce asociaz ecarui element o cheie unic, a a dup care poate regsit. Obiectele de acest tip nu pot contine chei duplia a cate i ecare cheie este asociat la un singur element. Ierarhia interfetelor s a derivate din Map este independent de ierarhia derivat din Collection. a a Denittai interfetei este prezentat mai jos: a public interface Map { // Metode cu caracter general int size(); boolean isEmpty(); void clear(); // Operatii la nivel de element Object put(Object key, Object value); Object get(Object key); Object remove(Object key); boolean containsKey(Object key); boolean containsValue(Object value); // Operatii la nivel de multime void putAll(Map t); // Vizualizari ale colectiei public Set keySet(); public Collection values(); public Set entrySet(); // Interfata pentru manipularea unei inregistrari public interface Entry { Object getKey(); Object getValue(); Object setValue(Object value); } }

166

CAPITOLUL 7. COLECTII

Clase care implementeaz interfat Map sunt HashMap, TreeMap i a a s Hashtable.

SortedMap este asemntoare cu interfata Map, la care se adaug fapa a a tul c multimea cheilor dintr-o astfel de colectie este mentinut ordonat a a a ascendent conform ordinii naturale, sau conform cu ordinea dat de un coma parator specicat la crearea colectiei. Este subclasa a interfetei Map, oferind metode suplimentare pentru: extragere de subtabele, aarea primei/ultimei chei, aarea comparatorului folosit pentru ordonare. Denitia interfetei este dat mai jos: a public interface SortedMap extends Map { // Extragerea de subtabele SortedMap subMap(Object fromKey, Object toKey); SortedMap headMap(Object toKey); SortedMap tailMap(Object fromKey); // Capete Object first(); Object last(); // Comparatorul folosit pentru ordonare Comparator comparator(); } Clasa care implementeaz aceast interfat este TreeMap. a a a

7.3

Implementri ale colectiilor a

Inainte de versiunea 1.2, exista un set de clase pentru lucrul cu colectii, a ns acestea nu erau organizate pe ierarhia de interfete prezentat sectiunea a n anterioar. Aceste clase sunt continuare disponibile i multe dintre ele a n s au fost adaptate aa fel at s se integreze noua abordare. Pe lng n s nc a n a a acestea au fost create noi clase corespunztoare interfetelor denite, chiar a dac functionalitatea lor era aproape identic cu cea a unei clase anterioare. a a

7.3. IMPLEMENTARI ALE COLECTIILOR

167

Clasele de baz care implementeaz interfete ce descriu colectii au numele a a de forma < Implementare >< Interf ata >, unde implementare se refer la a structura intern folosit pentru reprezentarea multimii, i sunt prezentate a a s tabelul de mai jos, n mpreun cu interfetele corespunztoare (clasele din a a vechiul model sunt trecute pe rndul de jos): a Interfata Set SortedSet List Clasa HashSet TreeSet ArrayList, LinkedList Vector Map HashMap Hashtable SortedMap TreeMap Aadar se observ existenta unor clase care ofer aceeai functionalite, s a a s cum ar ArrayList i Vector, HashMap i Hashtable. s s Pe lng organizarea ierarhic a interfetelor implementate, clasele ce dea a a scriu colectii sunt de asemenea concepute ntr-o manier ierarhic, ca gura a a n de mai jos: AbstractCollection - AbstractSet, AbstractList - HashSet, TreeSet... Vector-Stack AbstractMap - HashMap, TreeMap, HashTable In vechea ierarhie: Dictionary - Hashtable - Properties Evident, implementarea interfetelor este explicit realizat la nivelul super a claselor abstracte, acestea oferind de altfel i implementri concrete pentru s a multe din metodele denite de interfete. In general, clasele care descriu colectii au unele trsaturi comune, cum ar a : permit elementul null, sunt serializabile, au denit metoda clone, a

168

CAPITOLUL 7. COLECTII

au denit metoda toString, care returneaz o reprezentare ca ir de a a s caractere a colectiei respective, permit crearea de iteratori pentru parcurgere, au att constructor fr argumente ct i un constructor care accept a aa a s a ca argument o alt colectie a exceptnd clasele din arhitectura veche, nu sunt sincronizate (vezi Fire a de executie).

7.4

Folosirea ecient a colectiilor a

Dup cum am vazut, ecare interfat ce descrie o colectie are mai multe a a implementri. De exemplu, interfata List este implementat de clasele a a ArrayList i LinkedList, prima ind general mult mai folosit. De ce s n a exist atunci i clasa LinkedList ? Raspunsul const faptul c folosind a s a n a reprezentri diferite ale multimii gestionate putem obtine performante mai a bune functie de situatie, prin realizarea unor compromisuri n ntre spatiul necesar pentru memorarea datelor, rapiditatea regsirii acestora i timpul a s necesar actualizrii colectiei cazul unor modicri. a n a S considerm un exemplu ce creeaza o list folosind ArrayList, respectiv a a a LinkedList i execut diverse operatii pe ea, cronometrnd timpul necesar s a a realizrii acestora: a Listing 7.1: Comparare ArrayList - LinkedList
import java . util .*; public class TestEficienta { final static int N = 100000; public static void testAdd ( List lst ) { long t1 = System . curre ntTimeM illis () ; for ( int i =0; i < N ; i ++) lst . add ( new Integer ( i ) ) ; long t2 = System . curre ntTimeM illis () ; System . out . println ( " Add : " + ( t2 - t1 ) ) ; }

7.4. FOLOSIREA EFICIENTA A COLECTIILOR


public static void testGet ( List lst ) { long t1 = System . curre ntTimeMillis () ; for ( int i =0; i < N ; i ++) lst . get ( i ) ; long t2 = System . curre ntTimeMillis () ; System . out . println ( " Get : " + ( t2 - t1 ) ) ; } public static void testRemove ( List lst ) { long t1 = System . currentTimeM illis () ; for ( int i =0; i < N ; i ++) lst . remove (0) ; long t2 = System . currentTimeM illis () ; System . out . println ( " Remove : " + ( t2 - t1 ) ) ; } public static void main ( String args []) { System . out . println ( " ArrayList " ) ; List lst1 = new ArrayList () ; testAdd ( lst1 ) ; testGet ( lst1 ) ; testRemove ( lst1 ) ; System . out . println ( " LinkedList " ) ; List lst2 = new LinkedList () ; testAdd ( lst2 ) ; testGet ( lst2 ) ; testRemove ( lst2 ) ; } }

169

Timpii aproximativi de rulare pe un calculator cu performante medii, exprimati secunde, sunt dati tabelul de mai jos: n n ArrayList 0.12 0.01 12.05 LinkedList 0.14 87.45 0.01

add get remove

Aadar, adugarea elementelor este rapid pentru ambele tipuri de liste. s a a ArrayList ofer acces timp constant la elementele sale i din acest motiv a n s folosirea lui get este rapid, timp ce pentru LinkedList este extrem a n de lent, deoarece a ntr-o list a nlantuit accesul la un element se face prin a

170

CAPITOLUL 7. COLECTII

parcurgerea secvential a listei pn la elementul respectiv. a a a La operatiunea de eliminare, folosirea lui ArrayList este lent deoarece el a ementele rmase sufer un proces de reindexare (shift la stnga), timp a a a n ce pentru LinkedList este rapid i se face prin simpla schimbare a unei a s legturi. Deci, ArrayList se comport bine pentru cazuri care avem a a n nevoie de regsirea unor elemente la pozitii diferite list, iar LinkedList a n a functioneaza ecient atunci cnd facem multe operatii de modicare (tergeri, a s inserri). a

Concluzia nu este c una din aceste clase este mai bun dect cealalt, a a a a ci c exist diferente substantiale reprezentarea i comportamentul diferitelor a a n s implementri i c alegerea unei anumite clase pentru reprezentarea unei a s a multimi de elemente trebuie s se fac functie de natura problemei ce a a n trebuie rezolvat. a

7.5

Algoritmi polimorci

Algoritmii polimorci descrii aceast sectiune sunt metode denite s n a n clasa Collections care permit efectuarea unor operatii utile cum ar cutarea, a sortarea, etc. Caracterisiticile principale ale acestor algoritmi sunt: sunt metode de clas (statice); a au un singur argument de tip colectie; apelul lor general va de forma: Collections.algoritm(colectie, [argumente]); majoritatea opereaz pe liste dar i pe colectii arbitrare. a s Metodele mai des folosite din clasa Collections sunt: sort - sorteaz ascendent o list referitor la ordinea s natural sau la a a a a ordinea dat de un comparator; a shue - amestec elementele unei liste - opusul lui sort; a binarySearch - efectueaz cutarea ecient (binar) a unui element a a a a ntr-o list ordonat; a a

7.6. TIPURI GENERICE reverse - inverseaz ordinea elementelor dintr-o list; a a

171

ll - populeaza o lista cu un anumit element repetat de un numr de a ori; copy - copie elementele unei liste alta; n min - returneaz minimul dintr-o colectie; a max - returneaz maximul dintr-o colectie; a swap - interschimb elementele de la dou pozitii specicate ale unei a a liste; enumeration - returneaza o enumerare a elementelor dintr-o colectie; unmodiableTipColectie - returneaz o instant care nu poate moda a icat a colectiei respective; a synchronizedTipColectie - returneaz o instant sincronizat a unei a a a colectii (vezi Fire de executie).

7.6

Tipuri generice

Tipurile generice, introduse versiunea 1.5 a limbajului Java, simplic n a lucrul cu colectii, permitnd tipizarea elementelor acestora. Denirea unui a tip generic se realizeaz prin specicarea a ntre paranteze unghiulare a unui tip de date Java, efectul ind impunerea tipului respectiv pentru toate elementele colectiei: <TipDate>. S considerm un exemplu de utilizare a colectiilor a a nainte i dup introducerea tipurilor generice: s a // Inainte de 1.5 ArrayList list = new ArrayList(); list.add(new Integer(123)); int val = ((Integer)list.get(0)).intValue(); In exemplul de mai sus, lista denit poate contine obiecte de orice tip, dei a s am dori ca elementele s e doar numere a ntregi. Mai mult, trebuie s facem a cast explicit de la tipul Object la Integer atunci cnd prelum valoarea a a unui element. Folosind tipuri generice, putem rescrie secventa astfel:

172

CAPITOLUL 7. COLECTII

// Dupa 1.5, folosind tipuri generice ArrayList<Integer> list = new ArrayList<Integer>(); list.add(new Integer(123)); int val = list.get(0).intValue(); Dac utilizm i mecanismul de autoboxing, obtinem o variant mult sima a s a plicat a secventei initiale: a // Dupa 1.5, folosind si autoboxing ArrayList<Integer> list = new ArrayList<Integer>(); list.add(123); int val = list.get(0); In cazul folosirii tipurilor generice, ncercarea de a utiliza cadrul unei n colectii a unui element necorespunztor ca tip va produce o eroare la compi a lare, spre deosebire de varianta anterioar ce permitea doara aruncarea unor a exceptie de tipul ClassCastException cazul folosirii incorecte a tipurilor. n

7.7

Iteratori i enumerri s a

Enumerrile i iteratorii descriu modaliti pentru parcurgerea secvential a a s at a unei colectii, indiferent dac aceasta este indexat sau nu. Ei sunt descrii a a s de obiecte ce implementeaz interfetele Enumeration, respectiv Iterator a sau ListIterator. Toate clasele care implementeaz colectii au metode ce a returneaz o enumerare sau un iterator pentru parcurgerea elementelor lor. a Deoarece functionalitatea interfetei Enumeration se regsete Iterator, a s n aceasta din urm este preferat noile implementri ale colectiilor. a a n a Metodele uzuale ale acestor interfete sunt prezentate mai jos, mpreun a cu modalitatea lor de folosire, semnicatiile lor ind evidente: Enumeration: hasMoreElements, nextElement // Parcurgerea elementelor unui vector v Enumeration e = v.elements; while (e.hasMoreElements()) { System.out.println(e.nextElement()); } // sau, varianta mai concisa for (Enumeration e = v.elements();

7.7. ITERATORI SI ENUMERARI e.hasMoreElements();) { System.out.println(e.nextElement()); } Iterator: hasNext, next, remove // Parcurgerea elementelor unui vector // si eliminarea elementelor nule for (Iterator it = v.iterator(); it.hasNext();) { Object obj = it.next(); if (obj == null) it.remove(); } ListIterator: hasNext, hasPrevious, next, previous, remove, add, set // Parcurgerea elementelor unui vector // si inlocuirea elementelor nule cu 0 for (ListIterator it = v.listIterator(); it.hasNext();) { Object obj = it.next(); if (obj == null) it.set(new Integer(0)); }

173

Iteratorii simpli permit eliminarea elementului curent din colectia pe care o parcurg, cei de tip ListIterator permit i inserarea unui element la pozitia s curent, respectiv modicarea elementului curent, precum i iterarea ama s n bele sensuri. Iteratorii sunt preferati enumerrilor datorit posibilitii lor a a at de a actiona asupra colectiei pe care o parcurg prin metode de tip remove, add, set dar i prin faptul c denumirile metodelor sunt mai concise. s a

Atentie Deoarece colectiile sunt construite peste tipul de date Object, metodele de tip next sau prev ale iteratorilor vor returna tipul Object, ind responsabilitatea noastr de a face conversie (cast) la alte tipuri de date, dac este a a cazul.

174

CAPITOLUL 7. COLECTII

In exemplul de mai jos punem ntr-un vector numerele de la 1 la 10, le amestecm, dup care le parcurgem element cu element folosind un iterator, a a nlocuind numerele pare cu 0. Listing 7.2: Folosirea unui iterator
import java . util .*; class TestIterator { public static void main ( String args []) { ArrayList a = new ArrayList () ; // Adaugam numerele de la 1 la 10 for ( int i =1; i <=10; i ++) a . add ( new Integer ( i ) ) ; // Amestecam elementele colectiei Collections . shuffle ( a ) ; System . out . println ( " Vectorul amestecat : " + a ) ; // Parcurgem vectorul for ( ListIterator it = a . listIterator () ; it . hasNext () ; ) { Integer x = ( Integer ) it . next () ; // Daca elementul curent este par , il facem 0 if ( x . intValue () % 2 == 0) it . set ( new Integer (0) ) ; } System . out . print ( " Rezultat : " + a ) ; } }

Incepnd cu versiunea 1.5 a limbajului Java, exist o variant simplicat a a a a de utilizare a iteratorilor. Astfel, o secvent de genul: a ArrayList<Integer> list = new ArrayList<Integer>(); for (Iterator i = list.iterator(); i.hasNext();) { Integer val=(Integer)i.next(); // Proceseaza val ...

7.7. ITERATORI SI ENUMERARI } poate rescris astfel: a ArrayList<Integer> list = new ArrayList<Integer>(); for (Integer val : list) { // Proceseaza val ... }

175

176

CAPITOLUL 7. COLECTII

Capitolul 8 Serializarea obiectelor


8.1 Folosirea serializrii a

Denitie Serializarea este o metod ce permite transformarea unui obiect a ntr-o secventa de octeti sau caractere din care s poat refcut ulterior obiectul a a a original. Cu alte cuvinte, serializarea permite salvarea ntr-o manier unitar a a a tuturor informatiilor unui obiect pe un mediu de stocare extern programului. Procesul invers, de citire a unui obiect serializat pentru a-i reface starea original, se numete deserializare. Intr-un cadru mai larg, prin serializare a s se ntelege procesul de scriere/citire a obiectelor. Tipurile primitive pot de asemenea serializate. Utilitatea serializarii const urmtoarele aspecte: a n a Asigur un mecanism simplu de utilizat pentru salvarea i restaurarea a s a datelor. Permite persistenta obiectelor, ceea ce nseamna c durata de viata a a unui obiect nu este determinat de executia unui program care acesta a n este denit - obiectul poate exista i s ntre apelurile programelor care l folosesc. Acest lucru se realizeaz prin serializarea obiectului i scrierea a s lui pe disc nainte de terminarea unui program, apoi, la relansarea programului, obiectul va citit de pe disc i starea lui refacut. Acest s a 177

178

CAPITOLUL 8. SERIALIZAREA OBIECTELOR tip de persistent a obiectelor se numete persistent uoar, a s a s a ntruct a ea trebuie efectuat explicit de ctre programator i nu este realizat a a s a automat de ctre sistem. a

Compensarea diferentelor ntre sisteme de operare - transmiterea unor informatii ntre platforme de lucru diferite se realizeaz unitar, indea pendent de formatul de reprezentare a datelor, ordinea octetilor sau alte detalii specice sistemelor repective. Transmiterea datelor retea - Aplicatiile ce ruleaz retea pot comun a n nica ntre ele folosind uxuri pe care sunt trimise, respectiv receptionate obiecte serializate. RMI (Remote Method Invocation) - este o modalitate prin care metodele unor obiecte de pe o alt main pot apelate ca i cum acestea ar exa s a s ista local pe maina pe care ruleaz aplicatia. Atunci cnd este trimis s a a un mesaj ctre un obiect remote (de pe alt main), serializarea a a s a este utilizat pentru transportul argumentelor prin retea i pentru rea s turnarea valorilor. Java Beans - sunt componente reutilizabile, de sine stttoare ce pot aa utilizate medii vizuale de dezvoltare a aplicatiilor. Orice compon nent Bean are o stare denit de valorile implicite ale proprietilor a a at sale, stare care este specicat etapa de design a aplicatiei. Mediile a n vizuale folosesc mecanismul serializrii pentru asigurarea persistentei a componentelor Bean. Un aspect important al serializrii este c nu salveaz doar imaginea unui a a a obiect ci i toate referintele la alte obiecte pe care acesta le contine. Acesta s este un proces recusiv de salvare a datelor, ntruct celelalte obiectele referite a de obiectul care se serializeaz pot referi la rndul lor alte obiecte, i aa mai a a s s departe. Aadar referintele care construiesc starea unui obiect formeaz o s a ntreag retea, ceea ce a nseamn c un algoritm general de salvare a strii a a a unui obiect nu este tocmai facil. In cazul care starea unui obiect este format doar din valori ale unor n a variabile de tip primitiv, atunci salvarea informatiilor ncapsulate acel n obiect se poate face i prin salvarea pe rnd a datelor, folosind clasa s a DataOutputStream, pentru ca apoi s e restaurate prin metode ale clasei a DataInputStream, dar, aa cum am vazut, o asemenea abordare nu este s

8.1. FOLOSIREA SERIALIZARII

179

general sucient, deoarece pot aprea probleme cum ar : variabilele n a a membre ale obiectului pot instante ale altor obiecte, unele cmpuri pot a face referint la acelai obiect, etc. a s

Serializarea format binar a tipurilor primitive i a obiectelor se realn s izeaz prin intermediul uxurilor denite de clase specializate acest scop a n cu ar : ObjectOutputStream pentru scriere i ObjectInputStream pentru restaus rare. In continuare, prin termenul serializare ne vom referi doar la serializarea format binar. n

8.1.1

Serializarea tipurilor primitive

Serializarea tipurilor primitive poate realizat e prin intermediul uxua rilor DataOutputStream i DataInputStream, e cu ObjectOutputStream s i ObjectInputStream. Acestea implementeaz interfetele DataInput, res a spectiv DataOutput ce declar metode de tipul readTipPrimitiv, respectiv a writeTipPrimitiv pentru scrierea/citirea datelor primitive i a irurilor de s s caractere. Mai jos este prezentat un exemplu de serializare folosind clasa DataOutputStream: FileOutputStream fos = new FileOutputStream("test.dat"); DataOutputStream out = new DataOutputStream(fos); out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); out.flush(); fos.close(); Citirea informatiilor scrise exemplul de mai sus se va face astfel: n FileInputStream fis = new FileInputStream("test.dat"); DataInputStream in = new DataInputStream(fis); int i = in.readInt(); double d = in.readDouble(); boolean b = in.readBoolean();

180

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

String s = in.readUTF(); fis.close();

8.1.2

Serializarea obiectelor

Serializarea obiectelor se realizeaz prin intermediul uxurilor denite de a clasele ObjectOutputStream (pentru salvare) i ObjectInputStream s (pentru restaurare). Acestea sunt uxuri de procesare, ceea ce nseamna c vor folosite a mpreuna cu alte uxuri pentru scrierea/citirea efectiv a a datelor pe mediul extern pe care va salvat, sau de pe care va restaurat un obiect serializat. Mecanismul implicit de serializare a unui obiect va salva numele clasei obiectului, signatura clasei i valorile tuturor cmpurile serializabile ale obiecs a tului. Referintele la alte obiecte serializabile din cadrul obiectului curent vor duce automat la serializarea acestora iar referintele multiple ctre un acelai a s obiect sunt codicate utiliznd un algoritm care s poat reface reteaua de a a a obiecte la aceeai stare ca atunci cnd obiectul original a fost salvat. s a Clasele ObjectInputStream i ObjectOutputStream implementeaz interfetele s a ObjectInput, respectiv ObjectOutput care extind DataInput, respectiv DataOutput, ceea ce nseamn c, pe lng metodele dedicate serializrii obiectelor, vor a a a a a exista i metode pentru scrierea/citirea datelor primitive i a irurilor de s s s caractere. Metodele pentru serializarea obiectelor sunt: writeObject, pentru scriere i s readObject, pentru restaurare.

8.1.3

Clasa ObjectOutputStream

Scrierea obiectelor pe un ux de ieire este un proces extrem de simplu, s secventa uzual ind cea de mai jos: a ObjectOutputStream out = new ObjectOutputStream(fluxPrimitiv); out.writeObject(referintaObiect); out.flush(); fluxPrimitiv.close();

8.1. FOLOSIREA SERIALIZARII

181

Exemplul de mai jos construiete un obiect de tip Date i salveaz s s l a n ierul test.ser, s mpreun cu un obiect de tip String. Evident, ierul a s rezultat va contine informatiile reprezentate format binar. n FileOutputStream fos = new FileOutputStream("test.ser"); ObjectOutputStream out = new ObjectOutputStream(fos); out.writeObject("Ora curenta:"); out.writeObject(new Date()); out.flush(); fos.close(); Deoarece implementeaz interfata DataOutput, pe lnga metoda de scriere a a a obiectelor, clasa pune la dispozitie i metode de tipul writeTipPrimitiv s pentru serializarea tipurilor de date primitive i a irurilor de caractere, asts s fel at apeluri ca cele de mai jos sunt permise : nc out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); Metoda writeObject arunc exceptii de tipul IOException i derivate a s din aceasta, mai precis NotSerializableException dac obiectul primit ca a argument nu este serializabil, sau InvalidClassException dac sunt proba leme cu o clas necesar procesul de serializare. Vom vedea continuare a a n n c un obiect este serializabil dac este instant a unei clase ce implementeaz a a a a interfata Serializable.

8.1.4

Clasa ObjectInputStream

Odat ce au fost scrise obiecte i tipuri primitive de date pe un ux, citirea a s acestora i reconstruirea obiectelor salvate se va face printr-un ux de intrare s de tip ObjectInputStream. Acesta este de asemenea un ux de procesare i va trebui asociat cu un ux pentru citirea efectiv a datelor, cum ar s a FileInputStream pentru date salvate ntr-un ier. Secventa uzual pentru s a deserializare este cea de mai jos: ObjectInputStream in = new ObjectInputStream(fluxPrimitiv); Object obj = in.readObject(); //sau

182

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

TipReferinta ref = (TipReferinta)in.readObject(); fluxPrimitiv.close(); Citirea informatiilor scrise exemplul de mai sus se va face astfel: n FileInputStream fis = new FileInputStream("test.ser"); ObjectInputStream in = new ObjectInputStream(fis); String mesaj = (String)in.readObject(); Date data = (Date)in.readObject(); fis.close(); Trebuie observat c metoda readObject are tipul returnat Object, ceea a ce nseamn c trebuie realizat explicit conversia la tipul corespunzator a a a obiectului citit: Date date = in.readObject(); // gresit Date date = (Date)in.readObject(); // corect

Atentie Ca i la celelalte uxuri de date care implemeteaz interfata DataInput s a citirea dintr-un ux de obiecte trebuie s se fac exact ordinea carea a a n n acestea au fost scrise, altfel vor aprea evident exceptii procesul de desea n rializare.

Clasa ObjectInputStream implementeaz interfata DataInput deci, pe a lng metoda de citire a obiectelor, clasa pune la dispozitie i metode de a a s tipul readTipPrimitiv pentru citirea tipurilor de date primitive i a irurilor s s de caractere. int i = in.readInt(); double d = in.readDouble(); boolean b = in.readBoolean(); String s = in.readUTF();

8.2. OBIECTE SERIALIZABILE

183

8.2

Obiecte serializabile

Un obiect este serializabil dac i numai dac clasa din care face parte imas a plementeaz interfata Serializable. Aadar, dac dorim ca instantele unei a s a clase s poat serializate, clasa respectiv trebuie s implementeze, direct a a a a sau indirect, interfata Serializable.

8.2.1

Implementarea interfetei Serializable

Interfata Serializable nu contine nici o declaratie de metod sau constant, a a singurul ei scop ind de a identica clasele ale cror obiecte sunt serializabile. a Denitia sa complet este: a package java.io; public interface Serializable { // Nimic ! } Declararea claselor ale cror instante trebuie s e serializate este aadar a a s extrem de simpl, ind fcut prin simpla implementare a interfetei Serializable: a a a public class ClasaSerializabila implements Serializable { // Corpul clasei } Orice subclas a unei clase serializabile este la rndul ei serializabil, a a a ntruct implementeaz indirect interfata Serializable. a a In situatia care dorim s declarm o clas serializabil dar superclasa n a a a a sa nu este serializabil, atunci trebuie s avem vedere urmtoarele lucruri: a a n a Variabilele accesibile ale superclasei nu vor serializate, ind responsabilitatea clasei curente de a asigura un mecanism propriu pentru salvarea/restaurarea lor. Acest lucru va discutat sectiunea referitoare n la personalizarea serializrii. a Superclasa trebuie s aib obligatoriu un constructor accesibil fr ara a aa gumente, acesta ind utilizat pentru initializarea variabilelor motenite s procesul de restaurare al unui obiect. Variabilele proprii vor n initializate cu valorile de pe uxul de intrare. In lipsa unui constructor accesibil fr argumente pentru superclas, va generat o exceptie la aa a a executie.

184

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

In procesul serializrii, dac este alnit un obiect care nu implementeaz a a nt a interfata Serializable atunci va generat o exceptie de tipul NotSerializableException a ce va identica respectiva clas neserializabil. a a

8.2.2

Controlul serializrii a

Exist cazuri cnd dorim ca unele variabile membre ale unui obiect s nu e a a a salvate automat procesul de serializare. Acestea sunt cazuri comune atunci n cnd respectivele cmpuri reprezint informatii condentiale, cum ar paa a a role, sau variabile temporare pe care nu are rost s le salvm. Chiar declarate a a private cadrul clasei aceste cmpuri particip la serializare. Pentru ca un n a a cmp s nu e salvat procesul de serializare el trebuie declarat cu modia a n catorul transient i trebuie s e ne-static. De exemplu, declararea unei s a variabile membre temporare ar trebui facut astfel: a transient private double temp; // Ignorata la serializare Modicatorul static anuleaz efectul modicatorului transient. Cu a alte cuvinte, variabilele de clas particip obligatoriu la serializare. a a static transient int N; // Participa la serializare In exemplele urmtoare cmpurile marcate DA particip la serializare, a a a cele marcate NU, nu particip iar cele marcate cu Exceptie vor provoca a exceptii de tipul NotSerializableException. Listing 8.1: Modicatorii static i transient s
import java . io .*; public class Test1 implements Serializable { int x =1; transient int y =2; transient static int z =3; static int t =4; // DA // NU // DA // DA

public String toString () { return x + " , " + y + " , " + z + " , " + t ; } }

8.2. OBIECTE SERIALIZABILE

185

Dac un obiect ce trebuie serializat are referinte la obiecte neserializabile, a atunci va generat o exceptie de tipul NotSerializableException. a Listing 8.2: Membrii neserializabili
import java . io .*; class A { int x =1; } class B implements Serializable { int y =2; } public class Test2 implements Serializable { A a = new A () ; // Exceptie B b = new B () ; // DA public String toString () { return a . x + " , " + b . y ; } }

Atunci cnd o clas serializabila deriva dintr-o alt clas, salvarea cmpurilor a a a a a clasei printe se va face doar dac i aceasta este serializabil. In caz contrar, a as a subclasa trebuie s salveze explicit i cmpurile motenite. a s a s Listing 8.3: Serializarea cmpurilor motenite a s
import java . io .*; class C { int x =0; // Obligatoriu constructor fara argumente } class D extends C implements Serializable { int y =0; } public class Test3 extends D { public Test3 () { x = 1; // NU y = 2; // DA

186

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

} public String toString () { return x + " , " + y ; } }

Mai jos este descrisa o aplicatie care efectueaz salvarea i restaurarea a s unor obiecte din cele trei clase prezentate mai sus. Listing 8.4: Testarea serializrii a
import java . io .*; public class Exemplu { public static void test ( Object obj ) throws IOException { // Salvam FileOutputStream fos = new FileOutputStream ( " fisier . ser " ) ; ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ; out . writeObject ( obj ) ; out . flush () ; fos . close () ; System . out . println ( " A fost salvat obiectul : " + obj ) ; // Restauram FileInputStream fis = new FileInputStream ( " fisier . ser " ) ; ObjectInputStream in = new Obje ctInput Stream ( fis ) ; try { obj = in . readObject () ; } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } fis . close () ; System . out . println ( " A fost restaurat obiectul : " + obj ) ; } public static void main ( String args []) throws IOException { test ( new Test1 () ) ; try { test ( new Test2 () ) ; } catch ( N o t Se r i a l i z a b l e E x c e p t i o n e ) {

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR


System . out . println ( " Obiect neserializabil : " + e ) ; } test ( new Test3 () ) ; } }

187

Rezultatul acestui program va : A fost A fost Obiect A fost A fost salvat obiectul: 1, 2, 3, 4 restaurat obiectul: 1, 0, 3, 4 neserializabil: java.io.NotSerializableException: A salvat obiectul: 1, 2 restaurat obiectul: 0, 2

8.3

Personalizarea serializrii obiectelor a

Dezavantajul mecanismului implicit de serializare este c algoritmul pe care a se beazeaz, ind creat pentru cazul general, se poate comporta inecient a n anumite situatii: poate mult mai lent dect este cazul sau reprezentarea a binar generat pentru un obiect poate mult mai voluminoas dect ar a a a a trebui. In aceste situatii, putem s a nlocuim algoritmul implicit cu unul propriu, particularizat pentru o clas anume. De asemenea, este posibil a s extindem comportamentul implicit, adugnd i alte informatii necesare a a a s pentru serializarea unor obiecte. In majoritatea cazurilor mecanismul standard este sucient a, dup cum ns a am spus, o clas poate avea nevoie de mai mult control asupra serializrii. a a Personalizarea serializarii se realizeaz prin denirea ( a ntr-o clas seriala izabil!) a metodelor writeObject i readObject avnd exact signatura de a s a mai jos: private void writeObject(java.io.ObjectOutputStream stream) throws IOException private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException Metoda writeObject controleaz ce date sunt salvate iar readObject a controleaz modul care sunt restaurate obiectele, citind informatiile salvate a n i, eventual, modifcnd starea obiectelor citite astfel at ele s corespund s a nc a a

188

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

anumitor cerinte. In cazul care nu dorim s n a nlocuim complet mecanismul standard, putem s folosim metodele defaultWriteObject, respectiv a defaultReadObject care descriu procedurile implicite de serializare. Forma general de implementare a metodelor writeObject i readObject a s este: private void writeObject(ObjectOutputStream stream) throws IOException { // Procesarea campurilor clasei (criptare, etc.) ... // Scrierea obiectului curent stream.defaultWriteObject(); // Adaugarea altor informatii suplimentare ... } private void readObject(ObjectInputStream stream) throws IOException,ClassNotFoundException { // Restaurarea obiectului curent stream.defaultReadObject(); // Actualizarea starii obiectului (decriptare, etc.) // si extragerea informatiilor suplimentare ... } Metodele writeObject i readObject sunt responsabile cu serializarea s clasei care sunt denite, serializarea superclasei sale ind facut automat n a (i implicit). Dac a o clas trebuie sa-i coordoneze serializarea pros a ns a s prie cu serializarea superclasei sale, atunci trebuie s implementeze interfata a Externalizable.

8.3.1

Controlul versiunilor claselor

S presupunem c dorim s realizm o aplicatie care s in evidenta angajatilor a a a a at a unei companii. Evident, vom avean nevoie de o clas care s reprezinte a a

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

189

notiunea de angjat. O variant simplicat a acesteia ar putea arta astfel: a a a Listing 8.5: Prima variant a clasei Angajat a
import java . io .*; class Angajat implements Serializable { public String nume ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } }

Mai jos este prezentat o mic aplicatie care permite introducerea de a a angajati i salvarea lor s ntr-un ier. La ecare pornire a aplicatiei, vor s citite datele din ier astfel at programul va actualiza permanent lista s nc n a angajatilor cu noi persoane. Listing 8.6: Aplicatia de gestionare a angajatilor
import java . io .*; import java . util .*; public class GestiuneAngajati { // Lista angajatilor ArrayList ang = new ArrayList () ; public void citire () throws IOException { FileInputStream fis = null ; try { fis = new FileInputStream ( " angajati . ser " ) ; ObjectInputStream in = new Obje ctInput Stream ( fis ) ;

190

CAPITOLUL 8. SERIALIZAREA OBIECTELOR


ang = ( ArrayList ) in . readObject () ; } catch ( File NotF o u n d E x c e p t i o n e ) { System . out . println ( " Fisierul nou ... " ) ; } catch ( Exception e ) { System . out . println ( " Eroare la citirea datelor ... " ) ; e . printStackTrace () ; } finally { if ( fis != null ) fis . close () ; } System . out . println ( " Lista angajatilor :\ n " + ang ) ;

} public void salvare () throws IOException { FileOutputStream fos = new FileOutputStream ( " angajati . ser " ) ; ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ; out . writeObject ( ang ) ; } public void adaugare () throws IOException { BufferedReader stdin = new BufferedReader ( new InputStream Reader ( System . in ) ) ; while ( true ) { System . out . print ( " \ nNume : " ) ; String nume = stdin . readLine () ; System . out . print ( " Salariu : " ) ; int salariu = Integer . parseInt ( stdin . readLine () ) ; System . out . print ( " Parola : " ) ; String parola = stdin . readLine () ; ang . add ( new Angajat ( nume , salariu , parola ) ) ; System . out . print ( " Mai adaugati ? ( D / N ) " ) ; String raspuns = stdin . readLine () . toUpperCase () ; if ( raspuns . startsWith ( " N " ) ) break ; } } public static void main ( String args []) throws IOException { GestiuneAngajati app = new GestiuneAngajati () ;

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

191

// Incarcam angajatii din fisier app . citire () ; // Adaugam noi angajati app . adaugare () ; // Salvam angajatii inapoi fisier app . salvare () ; } }

Problema care se pune acum este urmtoarea. Dup introducerea unui a a numr sucient de mare de angajati ier, clasa Angajat este modia n s cat prin adugarea unei noi variabil membre care s retina adresa. La a a a a executia aplicatiei noastre, procedura de citire a angajatilor din ier nu s va mai functiona, producnd o exceptie de tipul InvalidClassException. a Aceastproblem ar aprut chiar dac variabila adugat era declarat de a a a a a a a tip transient. De ce se ampl acest lucru ? nt a Explicatia const faptul c mecanismul de serializare Java este foarte a n a atent cu signatura claselor serializate. Pentru ecare obiect serializat este calculat automat un numr reprezentat pe 64 de biti, care reprezint un fel de a a amprent a clasei obiectului. Acest numr, denumit serialVersionUID, a a este generat pornind de la diverse informatii ale clasei, cum ar variabilele sale membre, (dar nu numai) i este salvat procesul de serializare s n mpreun a cu celelalte date. In plus, orice modicare semnicativ a clasei, cum ar a adugarea unui nou cmp, va determina modicarea numrului su de a a a a versiune. La restaurarea unui obiect, numrul de versiune salvat forma serializat a n a va regsit i comparat cu noua semntur a clasei obiectului. In cazul a s a a n care acestea nu sunt egale, va generat o exceptie de tipul a InvalidClassException i deserializarea nu va fcut. s a a Aceast abordare extrem de precaut este foarte util pentru prevenirea a a a unor anomalii ce pot aprea cnd dou versiuni de clase sunt incompatia a a bile, dat poate suprtoare atunci cnd modicrile aduse clasei nu stric aa a a a compatibilitatea cu vechea versiune. In aceast situatie trebuie s comua a nicm explicit c cele dou clase sunt compatibile. Acest lucru se realizeaz a a a a prin setarea manual a variabilei serialVersionUID cadrul clasei dorite, a n adugnd pur i simplu cmpul: a a s a

192

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

static final long serialVersionUID = /* numar_serial_clasa */; Prezenta variabilei serialVersionUID printre membrii unei clase va in forma algoritmul de serialzare c nu mai calculeze numrul de serie al clasei, a a ci s-l foloseasc pe cel specicat de noi. Cum putem aa a numrul de sea a ns a rie al vechii clase Angajat care a fost folosit anterior la salvarea angajatilor a ? Utilitarul serialVer permite generarea numrului serialVersionUID pena tru o clas specicat. Aadar, trebuie s recompilm vechea clas Angajat a a s a a a i s-i am numrul de serie astfel: serialVer Angajat. Rezultatul va : s a a a Angajat: static final long serialVersionUID = 5653493248680665297L; Vom rescrie noua clas Angajat astfel at s e compatibil cu cea a nc a a veche astfel: Listing 8.7: Variant compatibil a clasei Angajat a a
import java . io .*; class Angajat implements Serializable { static final long serialVersionUID = 56 534 93 248 680 665 29 7 L ; public String nume , adresa ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . adresa = " Iasi " ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } }

Aplicatia noastr va functiona acum, a rubrica adres nu va initializat a ns a a nici un fel (va null), deoarece ea nu exista formatul original. La noua n n

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

193

salvare a datelor, vor serializate i informatiile legate de adres (evident, s a trebuie a s le citim de la tastatur...) ns a a

8.3.2

Securizarea datelor

Dup cum am vzut membrii privati, cum ar parola din exemplul de mai a a sus, particip la serializare. Problema const faptul c, dei format a a n a s n binar, informatiile unui obiect serializat nu sunt criptate nici un fel i pot n s regsite cu uurint, ceea ce poate reprezenta un inconvenient atunci cnd a s a a exist cmpuri condentiale. a a Rezolvarea acestei probleme se face prin modicarea mecanismului implicit de serializare, implementnd metodele readObject i writeObject, a s precum i prin utilizarea unei functii de criptare a datelor. Varianta securs izat a clasei Angajat din exemplul anterior ar putea arta astfel: a a Listing 8.8: Varianta securizat a clasei Angajat a
import java . io .*; class Angajat implements Serializable { static final long serialVersionUID = 5 65 349 324 868 06 652 97 L ; public String nume , adresa ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . adresa = " Iasi " ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } static String criptare ( String input , int offset ) { StringBuffer sb = new StringBuffer () ; for ( int n =0; n < input . length () ; n ++) sb . append (( char ) ( offset + input . charAt ( n ) ) ) ; return sb . toString () ;

194
}

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

private void writeObject ( Ob je ct Ou tpu tS tr ea m stream ) throws IOException { parola = criptare ( parola , 3) ; stream . defaultWri te Ob je ct () ; parola = criptare ( parola , -3) ; } private void readObject ( O bjectIn putStre am stream ) throws IOException , C l a s s N o t F o u n d E x c e p t i o n { stream . defaultRead Object () ; parola = criptare ( parola , -3) ; } }

8.3.3

Implementarea interfetei Externalizable

Pentru un control complet, explicit, al procesului de serializare, o clas trea buie s implementeze interfata Externalizable. Pentru instante ale acestor a clase doar numele clasei este salvat automat pe uxul de obiecte, clasa ind responsabil cu scrierea i citirea membrilor si i trebuie s se coordoneze a s a s a cu superclasele ei. Denitia interfetei Externalizable este: package java.io; public interface Externalizable extends Serializable { public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; } Aadar, aceste clase trebuie s implementeze obligatoriu metodele writes a External i readExternal care se va face serializarea complet a obiectelor s n a i coordonarea cu superclasa ei. s Uzual, interfata Externalizable este folosit situatii care se dorete a n n s mbuntirea performantelor algoritmului standard, mai exact creterea vitezei a at s procesului de serializare.

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

195

Mai jos este prezentat o clas simpl i modalitatea de rescriere a sa a a a s folosind interfata Externalizable: Listing 8.9: Serializare implicit a
import java . io .*; class Persoana implements Serializable { int cod ; String nume ; public Persoana ( String nume , int cod ) { this . nume = nume ; this . cod = cod ; } }

Listing 8.10: Serializare proprie


import java . io .*; class Persoana implements Externalizable { int cod ; String nume ; public Persoana ( String nume , int cod ) { this . nume = nume ; this . cod = cod ; } public void writeExternal ( ObjectOutput s ) throws IOException { s . writeUTF ( nume ) ; s . writeInt ( cod ) ; } public void readExternal ( ObjectInput s ) throws ClassNotFoundException , IOException { nume = s . readUTF () ; cod = s . readInt () ; } }

196

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

8.4

Clonarea obiectelor

Se tie c nu putem copia valoarea unui obiect prin instructiunea de atribuire. s a O secventa de forma: TipReferinta o1 = new TipReferinta(); TipReferinta o2 = o1; nu face dect s declare o nou variabil o2 ca referinta la obiectul referit de a a a a o1 i nu creeaz sub nici o form un nou obiect. s a a O posibilitate de a face o copie a unui obiect este folosirea metodei clone denit clasa Object. Aceasta creeaz un nou obiect i initializeaz toate a n a s a variabilele sale membre cu valorile obiectului clonat. TipReferinta o1 = new TipReferinta(); TipReferinta o2 = (TipReferinta) o1.clone(); Decienta acestei metode este c nu realizeaz duplicarea a a ntregii retele de obiecte corespunztoare obiectului clonat. In cazul care exist cmpuri a n a a referinta la alte obiecte, obiectele referite nu vor mai clonate la rndul lor. a O metod clone care s realizeze o copie efectiv a unui obiect, a a a mpreuna cu copierea tuturor obiectelor referite de cmpurile acelui obiect poate a implementat prin mecanismul serializrii astfel: a a public Object clone() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(baos); out.writeObject(this); out.close(); byte[] buffer = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(buffer); ObjectInputStream in = new ObjectInputStream(bais); Object ret = in.readObject(); in.close(); return ret; } catch (Exception e) {

8.4. CLONAREA OBIECTELOR System.out.println(e); return null; } }

197

198

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

Capitolul 9 Interfata grac cu utilizatorul a


9.1 Introducere

Interfata grac cu utilizatorul (GUI), este un termen cu eles larg care a nt se refer la toate tipurile de comunicare vizual a a ntre un program i utilizas torii si. Aceasta este o particularizare a interfetei cu utilizatorul (UI), prin a care vom ntelege conceptul generic de interactiune dintre program i utiliza s tori. Limbajul Java pune la dispozitie numeroase clase pentru implementarea diverselor functionalitati UI, a ne vom ocupa continuare de cele care ns n permit realizarea intefetei grace cu utilizatorul (GUI). De la aparitia limbajului Java, bibliotecile de clase care ofer servicii a grace au suferit probabil cele mai mari schimbri trecerea de la o vera n siune la alta. Acest lucru se datoreaz, pe de o parte dicultii legate de a at implementarea notiunii de portabilitate, pe de alt parte nevoii de a integra a mecanismele GUI cu tehnologii aprute i dezvoltate ulterior, cum ar Java a s Beans. In momentul actual, exist dou modaliti de a crea o aplicatie cu a a at interfata grac i anume: as AWT (Abstract Windowing Toolkit) - este API-ul initial pus la dispozitie ncepnd cu primele versiuni de Java; a Swing - parte dintr-un proiect mai amplu numit JFC (Java Foundation Classes) creat urma colaborrii dintre Sun, Netscape i IBM, n a s Swing se bazeaz pe modelul AWT, extinznd functionalitatea acestuia a a i adugnd sau s a a nlocuind componente pentru dezvoltarea aplicatiilor GUI. 199

200

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Aadar, este de preferat ca aplicatiile Java s e create folosind tehnologia s a Swing, aceasta punnd la dispozitie o palet mult mai larg de faciliti, a a a at a nu vom renunta complet la AWT deoarece aici exist clase esentiale, ns a reutilizate Swing. n In acest capitol vom prezenta clasele de baz i mecanismul de tratare a as evenimentelor din AWT, deoarece va simplicat procesul de elegere a nt dezvoltrii unei aplicatii GUI, dup care vom face trecerea la Swing. a a In principiu, crearea unei aplicatii grace presupune urmtoarele lucruri: a Design Crearea unei suprafete de aare (cum ar o fereastr) pe care vor s a aezate obiectele grace (componente) care servesc la comunis carea cu utilizatorul (butoane, controale pentru editarea textelor, liste, etc); Crearea i aezarea componentelor pe suprafata de aare la pozitiile s s s corespunztoare; a Functionalitate Denirea unor actiuni care trebuie s se execute momentul cnd a n a utilizatorul interactioneaz cu obiectele grace ale aplicatiei; a Ascultarea evenimentelor generate de obiecte momentul n interactiunii cu utilizatorul i executarea actiunilor corespunztoare, s a aa cum au fost ele denite. s

9.2

Modelul AWT

Pachetul care ofer componente AWT este java.awt. a Obiectele grace sunt derivate din Component, cu exceptia meniurilor care descind din clasa MenuComponent. Aadar, prin notiunea de component s a vom ntelege continuare orice obiect care poate avea o reprezentare grac n a i care poate interactiona cu utilizatorul. Exemple de componente sunt feres strele, butoanele, listele, bare de delare, etc. Toate componentele AWT sunt dente de clase proprii ce se gasesc pachetul java.awt, clasa Component n ind superclasa abstract a tuturor acestor clase. a Crearea obiectelor grace nu realizeaz automat i aarea lor pe ecran. a s s Mai ai ele trebuie aezate pe o suprafata de aare, care poate o fereastr nt s s a

9.2. MODELUL AWT

201

sau un applet, i vor deveni vizibile momentul care suprafata pe care sunt s n n aate va vizibil. O astfel de suprafat pe care sunt plasate componente s a a se mai numete container i reprezint o instant a unei clase derivate din s s a a Container. Clasa Container este o subclas aparte a lui Component, ind a la rndul ei superclasa tuturor suprafetelor de aare Java. a s Aa cum am vzut, interfat grac servete interactiunii cu utilizatorul. s a a a s De cele mai multe ori programul trebuie s fac o anumit prelucrare a a a n momentul care utilizatorul a efectuat o actiune i, prin urmare, compon s nentele trebuie s genereze evenimente functie de actiunea pe care au a n suferit-o (actiune transmis de la tastatur, mouse, etc.). Incepnd cu ver a a a siunea 1.1 a limbajului Java, evenimentele sunt instante ale claselor derivate din AWTEvent. Aadar, un eveniment este produs de o actiune a utilizatorului asupra unui s obiect grac, deci evenimentele nu trebuie generate de programator. In schimb, ntr-un program trebuie specicat codul care se execut la aparitia a unui eveniment. Tratarea evenimentelor se realizeaz prin intermediul unor a clase de tip listener (asculttor, consumator de evenimente), clase care sunt a denite pachetul java.awt.event. In Java, orice component poate conn a suma evenimentele generate de o alt component (vezi Tratarea evenia a mentelor). S considerm un mic exemplu, care crem o fereastr ce contine dou a a n a a a butoane. Listing 9.1: O fereastr cu dou butoane a a
import java . awt .*; public class ExempluAWT1 { public static void main ( String args []) { // Crearea ferestrei - un obiect de tip Frame Frame f = new Frame ( " O fereastra " ) ; // Setarea modului de dipunere a componentelor f . setLayout ( new FlowLayout () ) ; // Crearea celor doua butoane Button b1 = new Button ( " OK " ) ; Button b2 = new Button ( " Cancel " ) ; // Adaugarea butoanelor f . add ( b1 ) ;

202

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL


f . add ( b2 ) ; f . pack () ; // Afisarea fereastrei f . show () ;

} }

Dup cum veti observa la executia acestui program, att butoanele adugate a a a de noi ct i butonul de a s nchidere a ferestrei sunt functionale, adic pot a apasate, dar nu realizeaz nimic. Acest lucru se ampl deoarece nu am a nt a specicat nicieri codul care trebuie s se execute la apsarea acestor bua a a toane. De asemenea, mai trebuie remarcat c nu am specicat nicieri dimensiua a nile ferestrei sau ale butoanelor i nici pozitiile acestea s e plasate. Cu s n a toate acestea ele sunt plasate unul lnga celalalt, fr s se suprapun iar a aa a a suprafata fereastrei este sucient de mare ct s cuprind ambele obiecte. a a a Aceste fenomene sunt provocate de un obiect special de tip FlowLayout pe care l-am specicat i care se ocup cu gestionarea ferestrei i cu plasarea s a s componentelor ntr-o anumit ordine pe suprafata ei. Aadar, modul de a s aranjare nu este o caracteristic a suprafetei de aare ci, ecare container a s are asociat un obiect care se ocup cu dimensionarea i dispunerea compoa s nentelor pe suprafata de aare i care se numeste gestionar de pozitionare s s (layout manager) (vezi Gestionarea pozitionrii). a

9.2.1

Componentele AWT

Dup cum am spus deja, toate componentele AWT sunt dente de clase a proprii ce se gasesc pachetul java.awt, clasa Component ind superclasa n abstracta a tuturor acestor clase. Button - butoane cu eticheta format dintr-un text pe o singur linie; a a Canvas - suprafat pentru desenare; a Checkbox - component ce poate avea dou stri; mai multe obiecte a a a de acest tip pot grupate folosind clasa CheckBoxGroup; Choice - liste care doar elementul selectat este vizibil i care se n s deschid la apsarea lor; a

9.2. MODELUL AWT

203

Container - superclasa tuturor suprafetelor de aare (vezi Suprafete s de aare); s Label - etichete simple ce pot contine o singur linie de text needitabil; a List - liste cu selectie simpl sau multipl; a a Scrollbar - bare de delare orizontale sau verticale; TextComponent - superclasa componentelor pentru editarea textului: TextField (pe o singur linie) i TextArea (pe mai multe linii). a s Mai multe informatii legate de aceste clase vor prezentate sectiunea n Folosirea componentelor AWT. Din cauza unor diferente esentiale implementarea meniurilor pe diferite n platforme de operare, acestea nu au putut integrate ca obiecte de tip Component, superclasa care descrie meniuri ind MenuComponent (vezi Meniuri).

Componentele AWT au peste 100 de metode comune, motenite din clasa s Component. Acestea servesc uzual pentru aarea sau setarea atributelor obiectelor, cum ar : dimensiune, pozitie, culoare, font, etc. i au formatul s general getProprietate, respectiv setProprietate. Cele mai folosite, grupate pe tipul proprietii gestionate sunt: at Pozitie getLocation, getX, getY, getLocationOnScreen setLocation, setX, setY Dimensiuni getSize, getHeight, getWidth setSize, setHeight, setWidth Dimensiuni i pozitie s getBounds setBounds Culoare (text i fundal) s getForeground, getBackground setForeground, setBackground

204

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Font getFont setFont Vizibilitate setVisible isVisible Interactivitate setEnabled isEnabled

9.2.2

Suprafete de aare (Clasa Container) s

Crearea obiectelor grace nu realizeaz automat i aarea lor pe ecran. Mai a s s ai ele trebuie aezate pe o suprafat, care poate o fereastr sau suprafata nt s a a unui applet, i vor deveni vizibile momentul care suprafata respectiv s n n a va vizibil. O astfel de suprafata pe care sunt plasate componentele se a numete suprafat de aare sau container i reprezint o instanta a unei clase s a s s a derivat din Container. O parte din clasele a cror printe este Container a a a este prezentat mai jos: a Window - este superclasa tututor ferestrelor. Din aceast clas sunt a a derivate: Frame - ferestre standard; Dialog - ferestre de dialog modale sau nemodale; Panel - o suprafat fr reprezentare grac folosit pentru gruparea a a a a a altor componente. Din aceast clas deriv Applet, folosit pentru a a a a crearea appleturilor. ScrollPane - container folosit pentru implementarea automat a derulrii a a pe orizontal sau vertical a unei componente. a a Aadar, un container este folosit pentru a aduga componente pe suprafata s a lui. Componentele adugate sunt memorate a ntr-o list iar pozitiile lor din a aceast list vor deni ordinea de traversare front-to-back a acestora a a n cadrul containerului. Dac nu este specicat nici un index la adugarea unei a a componente, atunci ea va adaugat pe ultima pozitie a listei. a

9.2. MODELUL AWT

205

Clasa Container contine metodele comune tututor suprafetelor de aare. s Dintre cele mai folosite, amintim: add - permite adugarea unei componente pe suprafata de aare. a s O component nu poate apartine dect unui singur container, ceea ce a a nseamn c pentru a muta un obiect dintr-un container altul trebuie a a n sa-l eliminam mai ai de pe containerul initial. nt remove - elimin o component de pe container; a a setLayout - stabilete gestionarul de pozitionare al containerului (vezi s Gestionarea pozitionrii); a getInsets - determin distanta rezervat pentru marginile suprafetei a a de aare; s validate - forteaz containerul s reaeze toate componentele sale. a a s Aceast metod trebuie apelat explicit atunci cnd adugm sau eliminm a a a a a a a componente pe suprafata de aare dup ce aceasta a devenit vizibil. s a a Exemplu: Frame f = new Frame("O fereastra"); // Adaugam un buton direct pe fereastra Button b = new Button("Hello"); f.add(b); // Adaugam doua componente pe un panel Label et = new Label("Nume:"); TextField text = new TextField(); Panel panel = new Panel(); panel.add(et); panel.add(text); // Adaugam panel-ul pe fereastra // si, indirect, cele doua componente f.add(panel);

206

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

9.3

Gestionarea pozitionrii a

S considerm mai ai un exemplu de program Java care aeaz 5 butoane a a nt s a pe o fereastr: a Listing 9.2: Pozitionarea a 5 butoane
import java . awt .*; public class TestLayout { public static void main ( String args []) { Frame f = new Frame ( " Grid Layout " ) ; f . setLayout ( new GridLayout (3 , 2) ) ; // * Button Button Button Button Button b1 b2 b3 b4 b5 = = = = = new new new new new Button ( " Button 1 " ) ; Button ( " 2 " ) ; Button ( " Button 3 " ) ; Button ( " Long - Named Button 4 " ) ; Button ( " Button 5 " ) ;

f . add ( b1 ) ; f . add ( b2 ) ; f . add ( b3 ) ; f . add ( b4 ) ; f . add ( b5 ) ; f . pack () ; f . show () ; } }

Fereastra aata de acest program va arta astfel: s a

S modicm acum linia marcata cu * ca mai jos, lsnd neschimbat a a aa restul programului: Frame f = new Frame("Flow Layout"); f.setLayout(new FlowLayout()); Fereastra aat dup aceast modicare va avea o cu totul altfel de s a a a dispunere a componentelor sale:

9.3. GESTIONAREA POZITIONARII

207

Motivul pentru care cele dou ferestre arat att de diferit este c folosesc a a a a gestionari de pozitionare diferiti: GridLayout, respectiv FlowLayout. Denitie Un gestionar de pozitionare (layout manager) este un obiect care con troleaz dimensiunea i aranjarea (pozitia) componentelor unui container. a s Aadar, modul de aranjare a componentelor pe o suprafata de aare s s nu este o caracteristic a containerului. Fiecare obiect de tip Container a (Applet, Frame, Panel, etc.) are asociat un obiect care se ocup cu disa punerea componentelor pe suprafata sa i anume gestionarul su de pozitionare. s a Toate clasele care instantiaza obiecte pentru gestionarea pozitionrii imple a menteaz interfat LayoutManager. a a La instantierea unui container se creeaz implicit un gestionar de pozitionare a asociat acestuia. De exemplu, pentru o fereastr gestionarul implict este de a tip BorderLayout, timp ce pentru un panel este de tip FlowLayout. n

9.3.1

Folosirea gestionarilor de pozitionare

Aa cum am vzut, orice container are un gestionar implicit de pozitionare s a un obiect care implemeneaz interfata LayoutManager, acesta indu-i ataat a s automat la crearea sa. In cazul care acesta nu corespunde necesitilor n at noastre, el poate schimbat cu uurint. Cei mai utilizati gestionari din s a pachetul java.awt sunt: FlowLayout BorderLayout GridLayout CardLayout GridBagLayout

208

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Pe lng acetia, mai exist i cei din modelul Swing care vor prezentati a a s as capitolul dedicat dezvoltrii de aplicatii GUI folosind Swing. n a Ataarea explicit a unui gestionar de pozitionare la un container se face s a cu metoda setLayout a clasei Container. Metoda poate primi ca parametru orice instant a unei clase care implementeaz interfat LayoutManager. a a a Secventa de ataare a unui gestionar pentru un container, particularizat s a pentru FlowLayout, este: FlowLayout gestionar = new FlowLayout(); container.setLayout(gestionar); // sau, mai uzual: container.setLayout(new FlowLayout()); Programele nu apeleaz general metode ale gestionarilor de pozitionare, a n dar cazul cnd avem nevoie de obiectul gestionar putem obtine cu metoda n a l getLayout din clasa Container. Una din facilitile cele mai utile oferite de gestionarii de pozitionare at este rearanjarea componentele unui container atunci cnd acesta este reda imesionat. Pozitiile i dimensiunile componentelor nu sunt xe, ele ind s ajustate automat de ctre gestionar la ecare redimensionare astfel at s a nc a ocupe ct mai estetic suprafata de aare. Cum sunt determinate a a s ns dimensiunile implicite ale componentelor ? Fiecare clas derivat din Component poate implementa metodele getPrea a ferredSize, getMinimumSize i getMaximumSize care s returneze dis a mensiunea implicit a componentei respective i limitele afara crora coma s n a ponenta nu mai poate desenat. Gestionarii de pozitionare vor apela aceste a metode pentru a calcula dimensiunea la care vor aa o component. s a Sunt a situatii cnd dorim s plasm componentele la anumite pozitii ns a a a xe iar acestea s ramna acolo chiar dac redimensionm containerul. Folosind a a a a un gestionar aceast pozitionare absolut a componentelor nu este posibil i a a as deci trebuie cumva s renuntm la gestionarea automat a containerul. Acest a a a lucru se realizeaz prin trimitera argumentului null metodei setLayout: a // pozitionare absoluta a componentelor in container container.setLayout(null); Folosind pozitionarea absolut, nu va mai a sucient s adugam cu a ns a a metoda add componentele container, ci va trebui s specicm pozitia i n a a s

9.3. GESTIONAREA POZITIONARII

209

dimensiunea lor - acest lucru era fcut automat de gestionarul de pozitionare. a container.setLayout(null); Button b = new Button("Buton"); b.setSize(10, 10); b.setLocation (0, 0); container.add(b); In general, se recomand folosirea gestionarilor de pozitionare toate a n situatiile cnd acest lucru este posibil, deoarece permit programului s aib a a a aceeai s nfatisare indiferent de platforma i rezolutia pe care este rulat. s Pozitionarea absolut poate ridica diverse probleme acest sens. a n

S analizam continuare pe ecare din gestionarii amintiti anterior. a n

9.3.2

Gestionarul FlowLayout

Acest gestionar aeaz componentele pe suprafata de aare ux liniar, mai s a s n precis, componentele sunt adugate una dup alta pe linii, limita spatiului a a n disponibil. In momentul cnd o component nu mai a a ncape pe linia curent se a trece la urmtoarea linie, de sus jos. Adugarea componentelor se face de a n a la stnga la dreapta pe linie, iar alinierea obiectelor cadrul unei linii poate a n de trei feluri: la stnga, la dreapta i pe centru. Implicit, componentele a s sunt centrate pe ecare linie iar distanta implicit a ntre componente este de 5 pixeli pe vertical i 5 pe orizontal. as a Este gestionarul implicit al containerelor derivate din clasa Panel deci i s al applet-urilor. Listing 9.3: Gestionarul FlowLayout
import java . awt .*; public class TestFlowLayout { public static void main ( String args []) { Frame f = new Frame ( " Flow Layout " ) ; f . setLayout ( new FlowLayout () ) ; Button b1 = new Button ( " Button 1 " ) ; Button b2 = new Button ( " 2 " ) ; Button b3 = new Button ( " Button 3 " ) ;

210

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL


Button b4 = new Button ( " Long - Named Button 4 " ) ; Button b5 = new Button ( " Button 5 " ) ; f . add ( b1 ) ; f . add ( b2 ) ; f . add ( b3 ) ; f . add ( b4 ) ; f . add ( b5 ) ; f . pack () ; f . show () ;

} }

Componentele ferestrei vor aate astfel: s

Redimensionnd fereastra astfel at cele cinci butoane s nu mai a nc a ncap a pe o linie, ultimele dintre ele vor trecute pe linia urmtoare: a

9.3.3

Gestionarul BorderLayout

Gestionarul BorderLayout mparte suprafata de aare cinci regiuni, core s n spunztoare celor patru puncte cardinale i centrului. O component poate a s a plasat oricare din aceste regiuni, dimeniunea componentei ind calculata a n astfel at s ocupe nc a ntreg spatiul de aare oferit de regiunea respectiv. s a Pentru a aduga mai multe obiecte grace a ntr-una din cele cinci zone, ele trebuie grupate prealabil n ntr-un panel, care va amplasat apoi regiunea n dorit (vezi Gruparea componentelor - clasa Panel). a Aadar, la adugarea unei componente pe o suprafat gestionat de BorderLayout, s a a a metoda add va mai primi pe lnga referinta componentei i zona care a s n aceasta va amplasat, care va specicat prin una din constantele clasei: a a NORTH, SOUTH, EAST, WEST, CENTER. BorderLayout este gestionarul implicit pentru toate containerele care descind din clasa Window, deci al tuturor tipurilor de ferestre.

9.3. GESTIONAREA POZITIONARII Listing 9.4: Gestionarul BorderLayout


import java . awt .*; public class TestBorderLayout { public static void main ( String args []) { Frame f = new Frame ( " Border Layout " ) ; // Apelul de mai jos poate sa lipseasca f . setLayout ( new BorderLayout () ) ; f . add ( new f . add ( new f . add ( new f . add ( new f . add ( new f . pack () ; f . show () ; } } Button ( " Nord " ) , BorderLayout . NORTH ) ; Button ( " Sud " ) , BorderLayout . SOUTH ) ; Button ( " Est " ) , BorderLayout . EAST ) ; Button ( " Vest " ) , BorderLayout . WEST ) ; Button ( " Centru " ) , BorderLayout . CENTER ) ;

211

Cele cinci butoane ale ferestrei vor aate astfel: s

La redimensionarea ferestrei se pot observa urmtoarele lucruri: nordul i a s sudul se redimensioneaz doar pe orizontal, estul i vestul doar pe vertical, a a s a timp ce centrul se redimensioneaz att pe orizontal ct i pe vertical. n a a a a s a Redimensionarea componentelor din ecare zon se face astfel at ele ocup a nc a toat zona containerului din care fac parte. a

9.3.4

Gestionarul GridLayout

Gestionarul GridLayout organizeaz containerul ca un tabel cu rnduri i a a s coloane, componentele ind plasate celulele tabelului de la stnga la n a dreapta, ncepnd cu primul rnd. Celulele tabelului au dimensiuni egale a a iar o component poate ocupa doar o singur celul. Numrul de linii i a a a a s coloane vor specicate constructorul gestionarului, dar pot modicate n

212

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

i ulterior prin metodele setRows, respectiv setCols. Dac numrul de linii s a a sau coloane este 0 (dar nu ambele acelai timp), atunci componentele vor n s plasate ntr-o singur coloan sau linie. De asemenea, distanta a a ntre componente pe orizontal i distanta as ntre rndurile tabelului pot specicate a constructor sau stabilite ulterior. n Listing 9.5: Gestionarul GridLayout
import java . awt .*; public class TestGridLayout { public static void main ( String args []) { Frame f = new Frame ( " Grid Layout " ) ; f . setLayout ( new GridLayout (3 , 2) ) ; f . add ( new f . add ( new f . add ( new f . add ( new f . add ( new f . add ( new f . pack () ; f . show () ; } } Button ( " 1 " ) ) ; Button ( " 2 " ) ) ; Button ( " 3 " ) ) ; Button ( " 4 " ) ) ; Button ( " 5 " ) ) ; Button ( " 6 " ) ) ;

Cele ase butoane ale ferestrei vor plasate pe trei rnduri i dou s a s a coloane, astfel:

Redimensionarea ferestrei va determina redimensionarea tuturor celulelor i deci a tuturor componentelor, att pe orizontal ct i pe vertical. s a a a s a

9.3.5

Gestionarul CardLayout

Gestionarul CardLayout trateaz componentele adugate pe suprafata sa a a ntr-o manier similar cu cea a dispunerii crtilor de joc a a a ntr-un pachet.

9.3. GESTIONAREA POZITIONARII

213

Suprafata de aare poate asemnat cu pachetul de crti iar ecare com s a a a ponent este o carte din pachet. La un moment dat, numai o singur coma a ponent este vizibil (cea de deasupra). a a Clasa dispune de metode prin care s poat aat o anumit coma a s a a ponent din pachet, sau s se poat parcurge secvential pachetul, ordinea a a a componentelor ind intern gestionarului. a Principala utilitate a acestui gestionar este utilizarea mai ecient a a spatiului disponibil situatii care componentele pot grupate aa n n n s fel at utilizatorul s interactioneze la un moment dat doar cu un anumit nc a grup (o carte din pachet), celelalte ind ascunse. O clas Swing care implementeaz un mecansim similar este JTabbedPane. a a Listing 9.6: Gestionarul CardLayout
import java . awt .*; import java . awt . event .*; public class TestCardLayout extends Frame implements ActionListener { Panel tab ; public TestCardLayout () { super ( " Test CardLayout " ) ; Button card1 = new Button ( " Card 1 " ) ; Button card2 = new Button ( " Card 2 " ) ; Panel butoane = new Panel () ; butoane . add ( card1 ) ; butoane . add ( card2 ) ; tab = new Panel () ; tab . setLayout ( new CardLayout () ) ; TextField tf = new TextField ( " Text Field " ) ; Button btn = new Button ( " Button " ) ; tab . add ( " Card 1 " , tf ) ; tab . add ( " Card 2 " , btn ) ; add ( butoane , BorderLayout . NORTH ) ; add ( tab , BorderLayout . CENTER ) ; pack () ; show () ;

214

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL


card1 . addActionListe ner ( this ) ; card2 . addActionListe ner ( this ) ;

} public void actionPerformed ( ActionEvent e ) { CardLayout gestionar = ( CardLayout ) tab . getLayout () ; gestionar . show ( tab , e . getActionCommand () ) ; } public static void main ( String args []) { TestCardLayout f = new TestCardLayout () ; f . show () ; } }

Prima carte este vizibil a

A doua carte este vizibil a

9.3.6

Gestionarul GridBagLayout

Este cel mai complex i exibil gestionar de pozitionare din Java. La fel ca s n cazul gestionarului GridLayout, suprafata de aare este considerat ca ind s a un tabel a, spre deosebire de acesta, numrul de linii i de coloane sunt ns a s determinate automat, functie de componentele amplasate pe suprafata de n aare. De asemenea, functie de componentele gestionate, dimensiunile s n celulelor pot diferite cu singurele restrictii ca pe aceeai linie s aib aceeai s a a s ime, iar pe coloan aib aceeai lime. Spre deosebire de GridLayout, nalt a a s at o component poate ocupa mai multe celule adiacente, chiar de dimensiuni a diferite, zona ocupat ind referit prin regiunea de aare a componentei a a s respective. Pentru a specica modul de aare a unei componente, acesteia este s i asociat un obiect de tip GridBagConstraints, care se specic diferite n a proprieti ale componentei referitoare la regiunea s de aare i la modul at a s s care va plasat aceast regiune. Legtura dintre o component i un n a n a a as obiect GridBagConstraints se realizeaz prin metoda setConstraints: a GridBagLayout gridBag = new GridBagLayout();

9.3. GESTIONAREA POZITIONARII

215

container.setLayout(gridBag); GridBagConstraints c = new GridBagConstraints(); //Specificam restrictiile referitoare la afisarea componentei . . . gridBag.setConstraints(componenta, c); container.add(componenta); Aadar, s nainte de a aduga o component pe suprafata unui container a a care are un gestionar de tip GridBagLayout, va trebui s specicm anumiti a a parametri (constrngeri) referitori la cum va plasat componenta respeca a tiv. Aceste constrngeri vor specicate prin intermediul unui obiect de tip a a GridBagConstraints, care poate refolosit pentru mai multe componente care au aceleai constrngeri de aare: s a s gridBag.setConstraints(componenta1, c); gridBag.setConstraints(componenta2, c); . . . Cele mai utilizate tipuri de constrngeri pot specicate prin intermediul a urmtoarelor variabile din clasa GridBagConstraints: a gridx, gridy - celula ce reprezint coltul stnga sus al componentei; a a gridwidth, gridheight - numrul de celule pe linie i coloan pe care a s a va aat componenta; s a fill - folosit pentru a specica dac o component va ocupa a a a ntreg spatiul pe care are destinat; valorile posibile sunt HORIZONTAL, VERTICAL, l BOTH, NONE; insets - distantele dintre component i marginile suprafetei sale de as aare; s anchor - folosit atunci cnd componenta este mai mic dect suprafata a a a a sa de aare pentru a forta o anumit dispunere a sa: nord, sud, est, s a vest, etc. weigthx, weighty - folosite pentru distributia spatiului liber; uzual au valoarea 1;

216

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Ca exemplu, s realizm o fereastr ca gura de mai jos. Pentru a a a a n simplica codul, a fost creat o metod responsabil cu setarea valorilor a a a gridx, gridy, gridwidth, gridheight i adugarea unei componente cu s a restrictiile stabilite pe fereastr. a

Listing 9.7: Gestionarul GridBagLayout


import java . awt .*; public class TestGridBag Layout { static Frame f ; static GridBagLayout gridBag ; static GridBagConstr aint s gbc ; static void adauga ( Component comp , int x , int y , int w , int h ) { gbc . gridx = x ; gbc . gridy = y ; gbc . gridwidth = w ; gbc . gridheight = h ; gridBag . setConstraints ( comp , gbc ) ; f . add ( comp ) ; } public static void main ( String args []) { f = new Frame ( " Test GridBagLayout " ) ; gridBag = new GridBagLayout () ; gbc = new GridBa gC ons tr ai nt s () ; gbc . weightx = 1.0; gbc . weighty = 1.0;

9.3. GESTIONAREA POZITIONARII


gbc . insets = new Insets (5 , 5 , 5 , 5) ; f . setLayout ( gridBag ) ;

217

Label mesaj = new Label ( " Evidenta persoane " , Label . CENTER ); mesaj . setFont ( new Font ( " Arial " , Font . BOLD , 24) ) ; mesaj . setBackground ( Color . yellow ) ; gbc . fill = GridBagConst ra in ts . BOTH ; adauga ( mesaj , 0 , 0 , 4 , 2) ; Label etNume = new Label ( " Nume : " ) ; gbc . fill = GridBagConst ra in ts . NONE ; gbc . anchor = GridBagCon st ra in ts . EAST ; adauga ( etNume , 0 , 2 , 1 , 1) ; Label etSalariu = new Label ( " Salariu : " ) ; adauga ( etSalariu , 0 , 3 , 1 , 1) ; TextField nume = new TextField ( " " , 30) ; gbc . fill = GridBagConst ra in ts . HORIZONTAL ; gbc . anchor = GridBagCon st ra in ts . CENTER ; adauga ( nume , 1 , 2 , 2 , 1) ; TextField salariu = new TextField ( " " , 30) ; adauga ( salariu , 1 , 3 , 2 , 1) ; Button adaugare = new Button ( " Adaugare " ) ; gbc . fill = GridBagConst ra in ts . NONE ; adauga ( adaugare , 3 , 2 , 1 , 2) ; Button salvare = new Button ( " Salvare " ) ; gbc . fill = GridBagConst ra in ts . HORIZONTAL ; adauga ( salvare , 1 , 4 , 1 , 1) ; Button iesire = new Button ( " Iesire " ) ; adauga ( iesire , 2 , 4 , 1 , 1) ; f . pack () ; f . show () ; } }

218

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

9.3.7

Gruparea componentelor (Clasa Panel)

Plasarea componentelor direct pe suprafata de aare poate deveni incomod s a cazul care avem multe obiecte grace. Din acest motiv, se recomand n n a gruparea componentelor nrudite ca functii astfel at s putem siguri c, nc a a indiferent de gestionarul de pozitionare al suprafetei de aare, ele se vor gsi s a mpreun. Gruparea componentelor se face panel-uri. a n Un panel este cel mai simplu model de container. El nu are o reprezentare vizibil, rolul su ind de a oferi o suprafat de aare pentru componente a a a s grace, inclusiv pentru alte panel-uri. Clasa care instantiaza aceste obiecte este Panel, extensie a superclasei Container. Pentru a aranja corespunztor a componentele grupate ntr-un panel, acestuia i se poate specica un gestionar de pozitionare anume, folosind metoda setLayout. Gestionarul implicit pen tru containerele de tip Panel este FlowLayout. Aadar, o aranjare ecient a componentelor unei ferestre s a nseamn: a gruparea componentelor nfratite (care nu trebuie s e despartite a de gestionarul de pozitionare al ferestrei) panel-uri; n aranjarea componentelor unui panel, prin specicarea unui gestionar de pozitionare corespunztor; a aranjarea panel-urilor pe suprafata ferestrei, prin specicarea gestionaru lui de pozitionare al ferestrei.

Listing 9.8: Gruparea componentelor


import java . awt .*; public class TestPanel { public static void main ( String args []) { Frame f = new Frame ( " Test Panel " ) ; Panel intro = new Panel () ; intro . setLayout ( new GridLayout (1 , 3) ) ; intro . add ( new Label ( " Text : " ) ) ; intro . add ( new TextField ( " " , 20) ) ; intro . add ( new Button ( " Adaugare " ) ) ; Panel lista = new Panel () ; lista . setLayout ( new FlowLayout () ) ; lista . add ( new List (10) ) ; lista . add ( new Button ( " Stergere " ) ) ;

9.4. TRATAREA EVENIMENTELOR

219

Panel control = new Panel () ; control . add ( new Button ( " Salvare " ) ) ; control . add ( new Button ( " Iesire " ) ) ; f . add ( intro , BorderLayout . NORTH ) ; f . add ( lista , BorderLayout . CENTER ) ; f . add ( control , BorderLayout . SOUTH ) ; f . pack () ; f . show () ; } }

9.4

Tratarea evenimentelor

Un eveniment este produs de o actiune a utilizatorului asupra unei compo nente grace i reprezint mecanismul prin care utilizatorul comunic efectiv s a a cu programul. Exemple de evenimente sunt: apsarea unui buton, modia carea textului ntr-un control de editare, nchiderea sau redimensionarea unei ferestre, etc. Componentele care genereaz anumite evenimente se mai a numesc i surse de evenimente. s Interceptarea evenimentelor generate de componentele unui program se realizeaz prin intermediul unor clase de tip listener (asculttor, consumator a a de evenimente). In Java, orice obiect poate consuma evenimentele generate de o anumit component grac. a a a

Aadar, pentru a scrie cod care s se execute momentul care utilizas a n n torul interactioneaz cu o component grac trebuie s facem urmtoarele a a a a a lucruri: s scriem o clas de tip listener care s asculte evenimentele produse a a a de acea component i cadrul acestei clase s implementm metode a s n a a specice pentru tratarea lor;

220

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

s comunicm componentei surs c respectiva clasa ascult evenia a a a i a mentele pe care le genereaz, cu alte cuvinte s a a nregistrm acea clas a a drept consumator al evenimentelor produse de componenta respectiv. a Evenimentele sunt, ca orice altceva Java, obiecte. Clasele care descriu n aceste obiecte se mpart mai multe tipuri functie de componenta care n n le genereaz, mai precis functie de actiunea utilizatorului asupra acesteia. a n Pentru ecare tip de eveniment exist o clas care instantiaz obiecte de a a a acel tip. De exemplu, evenimentul generat de actionarea unui buton este descris de clasa ActionEvent, cel generat de modicarea unui text de clasa TextEvent, etc. Toate aceste clase sunt derivate din superclasa AWTEvent, lista lor complet ind prezentat ulterior. a a O clas consumatoare de evenimente (listener) poate orice clas care a a specica declaratia sa c dorete s asculte evenimente de un anumit n a s a tip. Acest lucru se realizeaz prin implementarea unei interfete specice a ecrui tip de eveniment. Astfel, pentru ascultarea evenimentelor de tip a ActionEvent clasa respectiv trebuie s implementeze interfata ActionListener, a a pentru TextEvent interfat care trebuie implementata este TextListener, a etc. Toate aceste interfete sunt derivate din EventListener. Fiecare interfat denete una sau mai multe metode care vor apelate a s automat la aparitia unui eveniment: class AscultaButoane implements ActionListener { public void actionPerformed(ActionEvent e) { // Metoda interfetei ActionListener ... } } class AscultaTexte implements TextListener { public void textValueChanged(TextEvent e) { // Metoda interfetei TextListener ... } } Intruct o clas poate implementa oricte interfete, ea va putea s asculte a a a a evenimente de mai multe tipuri:

9.4. TRATAREA EVENIMENTELOR class Ascultator implements ActionListener, TextListener { public void actionPerformed(ActionEvent e) { ... } public void textValueChanged(TextEvent e) { ... } }

221

Vom vedea continuare metodele ecrei interfete pentru a ti ce trebuie n a s s implementeze o clas consumatoare de evenimente. a a Aa cum am spus mai devreme, pentru ca evenimentele unei componente s s e interceptate de ctre o instant a unei clase asculttor, aceast clas a a a a a a trebuie nregistrata lista asculttorilor componentei respective. Am spus n a lista, deoarece evenimentele unei componente pot ascultate de oricte clase, a cu conditia ca acestea s e a nregistrate la componenta respectiv. Inregisa trarea unei clase lista asculttorilor unei componente se face cu metode n a din clasa Component de tipul addTipEvenimentListener, iar eliminarea ei din aceast list cu removeTipEvenimentListener. a a

Sumariznd, tratarea evenimentelor Java se desfoar astfel: a n as a Componentele genereaz evenimente cnd ceva interesant se ampl; a a nt a Sursele evenimentelor permit oricrei clase s asculte evenimentele a a sale prin metode de tip addXXXListener, unde XXX este un tip de eveniment; O clas care ascult evenimente trebuie s implementeze interfete specia a a ce ecrui tip de eveniment - acestea descriu metode ce vor apelate a automat la aparitia evenimentelor.

9.4.1

Exemplu de tratare a evenimentelor

Inainte de a detalia aspectele prezentate mai sus, s considerm un exemplu a a de tratare a evenimentelor. Vom crea o fereastr care s contin dou bua a a a toane cu numele OK, repectiv Cancel. La apsarea ecrui buton vom a a scrie pe bara de titlu a ferestrei mesajul Ati apasat butonul .... Listing 9.9: Ascultarea evenimentelor a dou butoane a
import java . awt .*; import java . awt . event .*;

222

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setLayout ( new FlowLayout () ) ; setSize (200 , 100) ; Button b1 = new Button ( " OK " ) ; Button b2 = new Button ( " Cancel " ) ; add ( b1 ) ; add ( b2 ) ; Ascultator listener = new Ascultator ( this ) ; b1 . addActionListener ( listener ) ; b2 . addActionListener ( listener ) ; // Ambele butoane sunt ascultate de obiectul listener , // instanta a clasei Ascultator , definita mai jos } } class Ascultator implements ActionListener { private Fereastra f ; public Ascultator ( Fereastra f ) { this . f = f ; } // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { f . setTitle ( " Ati apasat " + e . getActionCommand () ) ; } } public class TestEvent1 { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test Event " ) ; f . show () ; } }

Nu este obligatoriu s denim clase speciale pentru ascultarea evenia mentelor. In exemplul de mai sus am denit clasa Ascultator pentru a intercepta evenimentele produse de cele dou butoane i din acest motiv a a s trebuit s trimitem ca parametru constructorului clasei o referinta la fereasa tra noastr. Mai simplu ar fost s folosim chiar clasa Fereastra pentru a a a trata evenimentele produse de componentele sale. Vom modica putin i s

9.4. TRATAREA EVENIMENTELOR

223

aplicatia pentru a pune evidenta o alt modalitate de a determina com n a ponenta generatoare a unui eveniment - metoda getSource. Listing 9.10: Tratarea evenimentelor ferestr n a
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { Button ok = new Button ( " OK " ) ; Button exit = new Button ( " Exit " ) ; int n =0; public Fereastra ( String titlu ) { super ( titlu ) ; setLayout ( new FlowLayout () ) ; setSize (200 , 100) ; add ( ok ) ; add ( exit ) ; ok . addActionListener ( this ) ; exit . addActionListener ( this ) ; // Ambele butoane sunt ascultate in clasa Fereastra // deci ascultatorul este instanta curenta : this } // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { if ( e . getSource () == exit ) System . exit (0) ; // Terminam aplicatia if ( e . getSource () == ok ) { n ++; this . setTitle ( " Ati apasat OK de " + n + " ori " ) ; } } } public class TestEvent2 { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test Event " ) ; f . show () ; } }

224

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Aadar, orice clas poate asculta evenimente de orice tip cu conditia s s a a implementeze interfetele specice acelor tipuri de evenimente.

9.4.2

Tipuri de evenimente

Evenimentele se mpart dou categorii: de nivel jos i semantice. n a s

Evenimentele de nivel jos reprezint o interactiune de nivel jos cum ar a o apsare de tast, micarea mouse-ului, sau o operatie asupra unei ferestre. a a s In tabelul de mai jos sunt enumerate clasele ce descriu aceste evenimente i s operatiunile efectuate (asupra unei componente) care le genereaz: a ComponentEvent Ascundere, deplasare, redimensionare, aare s ContainerEvent Adugare pe container, eliminare a FocusEvent Obtinere, pierdere foucs KeyEvent Apsare, eliberare taste, tastare a MouseEvent Operatiuni cu mouse-ul: click, drag, etc. WindowEvent Operatiuni asupra ferestrelor: minimizare, maximizare,etc. O anumit actiune a utilizatorului poate genera mai multe evenimente. a De exemplu, tastarea literei A va genera trei evenimente: unul pentru apsare, unul pentru eliberare i unul pentru tastare. In functie de necea s sitile aplicatie putem scrie cod pentru tratarea ecrui eveniment parte. at a n

Evenimentele semantice reprezint interactiunea cu o component a a GUI: apsarea unui buton, selectarea unui articol dintr-o list, etc. Clasele a a care descriu aceste tipuri de evenimente sunt: ActionEvent AdjustmentEvent ItemEvent TextEvent Actionare Ajustarea unei valori Schimbarea strii a Schimbarea textului

9.4. TRATAREA EVENIMENTELOR

225

Urmtorul tabel prezint componentele AWT i tipurile de evenimente a a s generate, prezentate sub forma interfetelor corespunztoare. Evident, eveni a mentele generate de o superclas, cum ar Component, se vor regsi i pentru a a s toate subclasele sale.

Component

Container Window Button List MenuItem TextField Choice Checkbox List CheckboxMenuItem Scrollbar TextField TextArea

ComponentListener FocusListener KeyListener MouseListener MouseMotionListener ContainerListener WindowListener ActionListener

ItemListener

AdjustmentListener TextListener

Observati c dei exist o singur clas MouseEvent, exist dou interfete a s a a a a a asociate MouseListener i MouseMotionListener. Acest lucru a fost fcut s a deoarece evenimentele legate de deplasarea mouse-ului sunt generate foarte frecvent i receptionarea lor poate avea un impact negativ asupra vitezei de s executie, situatia cnd tratarea acestora nu ne intereseaz i dorim s n a a s a tratm doar evenimente de tip click, de exemplu. a

Orice clas care trateaz evenimente trebuie s implementeze obligatoriu a a a metodele interfetelor corespunztoare. Tabelul de mai jos prezint, pentru a a ecare interfat, metodele puse la dispozitie i care trebuie implementate de a s ctre clasa asculttor. a a

226

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Interfat a ActionListener AdjustmentListener

Metode actionPerformed(ActionEvent e) adjustmentValueChanged(AdjustmentEvent e) componentHidden(ComponentEvent e) ComponentListener componentMoved(ComponentEvent e) componentResized(ComponentEvent e) componentShown(ComponentEvent e) ContainerListener componentAdded(ContainerEvent e) componentRemoved(ContainerEvent e) FocusListener focusGained(FocusEvent e) focusLost(FocusEvent e) ItemListener itemStateChanged(ItemEvent e) keyPressed(KeyEvent e) KeyListener keyReleased(KeyEvent e) keyTyped(KeyEvent e) mouseClicked(MouseEvent e) mouseEntered(MouseEvent e) MouseListener mouseExited(MouseEvent e) mousePressed(MouseEvent e) mouseReleased(MouseEvent e) MouseMotionListener mouseDragged(MouseEvent e) mouseMoved(MouseEvent e) TextListener textValueChanged(TextEvent e) windowActivated(WindowEvent e) windowClosed(WindowEvent e) windowClosing(WindowEvent e) WindowListener windowDeactivated(WindowEvent e) windowDeiconified(WindowEvent e) windowIconified(WindowEvent e) windowOpened(WindowEvent e)

In cazul care un obiect listener trateaz evenimente de acelai tip provon a s cate de componente diferite, este necesar s putem aa, cadrul uneia din a n metodele de mai sus, care este sursa evenimentului pe care tratm penl a tru a putea reactiona consecint. Toate tipurile de evenimente motenesc n a s metoda getSource care returneaz obiectul responsabil cu generarea evenia mentului. In cazul care dorim s diferentiem doar tipul componentei surs, n a a

9.4. TRATAREA EVENIMENTELOR putem folosi operatorul instanceof. public void actionPerformed(ActionEvent e) { Object sursa = e.getSource(); if (sursa instanceof Button) { // A fost apasat un buton Button btn = (Button) sursa; if (btn == ok) { // A fost apasat butonul ok } ... } if (sursa instanceof TextField) { // S-a apasat Enter dupa editarea textului TextField tf = (TextField) sursa; if (tf == nume) { // A fost editata componenta nume } ... } }

227

Pe lng getSource, obiectele ce descriu evenimente pot pune la dispozitie a a i alte metode specice care permit aarea de informatii legate de evenimens tul generat. De exemplu, ActionEvent contine metoda getActionCommand care, implicit, returneaz eticheta butonului care a fost apsat. Astfel de a a particulariti vor prezentate mai detaliat sectiunile dedicate ecrei at n a componente parte. n

9.4.3

Folosirea adaptorilor i a claselor anonime s

Am vazut c o clas care trateaz evenimente de un anumit tip trebuie s ima a a a plementeze interfata corespunztoare acelui tip. Aceasta a nseamn c trebuie a a s implementeze obligatoriu toate metodele denite de acea interfat, chiar a a dac nu specic nici un cod pentru unele dintre ele. Sunt a situatii cnd a a ns a acest lucru poate deveni suprator, mai ales atunci cnd nu ne intereseaz a a a dect o singura metod a interfetei. a a Un exemplu sugestiv este urmtorul: o fereastr care nu are specicat cod a a pentru tratarea evenimentelor sale nu poate nchis cu butonul standard a

228

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

marcat cu x din coltul dreapta sus i nici cu combinatia de taste Alt+F4. s Pentru a realiza acest lucru trebuie interceptat evenimentul de nchidere a ferestrei metoda windoClosing i apelat metoda dispose de n s a nchidere a ferestrei, sau System.exit pentru terminarea programului, cazul cnd n a este vorba de fereastra principal a aplicatiei. Aceasta a nseamn c trebuie a a s implementm interfata WindowListener care are nu mai putin de apte a a s metode. Listing 9.11: Implementarea interfetei WindowListener
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements WindowListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( this ) ; } // Metodele interfetei WindowListener public void windowOpened ( WindowEvent e ) {} public void windowClosing ( WindowEvent e ) { // Terminare program System . exit (0) ; } public void windowClosed ( WindowEvent e ) {} public void windowIconified ( WindowEvent e ) {} public void windowDeiconified ( WindowEvent e ) {} public void windowActivated ( WindowEvent e ) {} public void windowDeact ivated ( WindowEvent e ) {} } public class TestWind ow Lis te ne r { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test WindowListener " ) ; f . show () ; } }

Observati c trebuie s implementm toate metodele interfetei, chiar dac a a a a nu scriem nici un cod pentru unele dintre ele. Singura metod care ne interea seaz este windowClosing, care specicm ce trebuie fcut atunci cnd a n a a a utilizatorul doreste s a nchid fereastra. Pentru a evita scrierea inutil a a a

9.4. TRATAREA EVENIMENTELOR

229

acestor metode, exist o serie de clase care implementeaz interfetele de tip a a listener fr a specica nici un cod pentru metodele lor. Aceste clase se aa numesc adaptori. Un adaptor este o clas abstract care implementeaz o anumit interfat a a a a a fr a specica cod nici unei metode a interfetei. aa Scopul unei astfel de clase este ca la crearea unui asculttor de evenia mente, loc s implement o anumit interfat i implicit toate metodele n a a a a s sale, s extindem adaptorul corespunztor interfetei respective (dac are!) a a a i s supradenim doar metodele care ne intereseaz (cele care vrem s s a a n a scriem o anumit secvent de cod). a a De exemplu, adaptorul interfetei WindowListener este WindowAdapter iar folosirea acestuia este dat exemplul de mai jos: a n Listing 9.12: Extinderea clasei WindowAdapter
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new Ascultator () ) ; } } class Ascultator extends WindowAdapter { // Suprdefinim metodele care ne intereseaza public void windowClosing ( WindowEvent e ) { System . exit (0) ; } } public class TestWindowAdapter { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test WindowAdapter " ) ; f . show () ; } }

Avantajul clar al acestei modaliti de tratare a evenimentelor este reat ducerea codului programului, acesta devenind mult mai lizibil. Ins exist i a as dou dezavantaje majore. Dup cum ati observat fatde exemplul anterior, a a a

230

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

clasa Fereastra nu poate extinde WindowAdapter deoarece ea extinde deja clasa Frame i din acest motiv am construit o nou clas numit Ascultator. s a a a Vom vedea a c acest dezavantaj poate eliminat prin folosirea unei clase ns a anonime. Un alt dezavantaj este c orice greeal de sintax declararea unei metode a s a a n a interfetei nu va produce o eroare de compilare dar nici nu va supradeni metoda interfetei ci, pur i simplu, va crea o metod a clasei respective. s a class Ascultator extends WindowAdapter { // In loc de windowClosing scriem WindowClosing // Nu supradefinim vreo metoda a clasei WindowAdapter // Nu da nici o eroare // Nu face nimic ! public void WindowClosing(WindowEvent e) { System.exit(0); } } In tabelul de mai jos sunt dati toti adaptorii interfetelor de tip listener - se oberv c o interfat XXXListener are un adaptor de tipul XXXAdapter. a a a Interfetele care nu au un adaptor sunt cele care denesc o singur metod i a as prin urmare crearea unei clase adaptor nu are rostul. si Interfata ActionListener AdjustemnrListener ComponentListener ContainerListener FocusListener ItemListener KeyListener MouseListener MouseMotionListener TextListener WindowListener Adaptor nu are nu are ComponentAdapter ContainerAdapter FocusAdapter nu are KeyAdapter MouseAdapter MouseMotionAdapter nu are WindowAdapter

Stim c o clas intern este o clas declarat cadrul altei clase, iar a a a a a n clasele anonime sunt clase interne folosite pentru instantierea unui singur obiect de un anumit tip. Un exemplu tipic de folosire a lor este instantierea

9.4. TRATAREA EVENIMENTELOR

231

adaptorilor direct corpul unei clase care contine componente ale cror n a evenimente trebuie tratate. Listing 9.13: Folosirea adaptorilor i a claselor anonime s
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (400 , 400) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { // Terminam aplicatia System . exit (0) ; } }) ; final Label label = new Label ( " " , Label . CENTER ) ; label . setBackground ( Color . yellow ) ; add ( label , BorderLayout . NORTH ) ;

this . addMouseListener ( new MouseAdapter () { public void mouseClicked ( MouseEvent e ) { // Desenam un cerc la fiecare click de mouse label . setText ( " Click ... " ) ; Graphics g = Fereastra . this . getGraphics () ; g . setColor ( Color . blue ) ; int raza = ( int ) ( Math . random () * 50) ; g . fillOval ( e . getX () , e . getY () , raza , raza ) ; } }) ; this . ad dM ous eM oti on Li st e n e r ( new Mo us eM ot ion Ad ap te r () { public void mouseMoved ( MouseEvent e ) { // Desenam un punct la coordonatele mouse - ului Graphics g = Fereastra . this . getGraphics () ; g . drawOval ( e . getX () , e . getY () , 1 , 1) ; } }) ;

232

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL


this . addKeyListener ( new KeyAdapter () { public void keyTyped ( KeyEvent e ) { // Afisam caracterul tastat label . setText ( " Ati tastat : " + e . getKeyChar () + " " ) ; } }) ;

} } public class TestAdapters { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test adaptori " ) ; f . show () ; } }

9.5

Folosirea ferestrelor

Dup cum am vzut suprafetele de aare ale componentelor sunt extensii a a s ale clasei Container. O categorie aparte a acestor containere o reprezint a ferestrele. Acestea sunt descrise de clase derivate din Window, cele mai utilizate ind Frame i Dialog. s O aplicatie Java cu intefat grac va format din una sau mai multe a a a ferestre, una dintre ele ind numit fereastra principal. a a

9.5.1

Clasa Window

Clasa Window este rar utilizat mod direct deoarece permite doar crearea a n unor ferestre care nu au chenar i nici bar de meniuri. Este util atunci s a a cnd dorim s aam ferestre care nu interactioneaz cu utilizatorul ci doar a a s a ofer anumite informatii. a Metodele mai importante ale clasei Window, care sunt de altfel motenite s de toate subclasele sale, sunt date de mai jos: show - face vizibil fereastra. Implicit, o fereastr nou creat nu este a a a vizibil; a hide - face fereastra invizibil fr a o distruge a; pentru a redeveni a aa ns vizibila se poate apela metoda show;

9.5. FOLOSIREA FERESTRELOR isShowing - testeaz dac fereastra este vizibil sau nu; a a a

233

dispose - nchide) fereastra i i elibereaz toate resursele acesteia; s s a pack - redimensioneaz automat fereastra la o suprafata optim care a a s cuprind toate componentele sale; trebuie apelat general dup a a a n a adugarea tuturor componentelor pe suprafata ferestrei. a getFocusOwner - returneaz componenta ferestrei care are focus-ul a (dac fereastra este activ). a a

9.5.2

Clasa Frame

Este derivat a clasei Window i este folosit pentru crearea de ferestre indea s a pendente i functionale, eventual continnd o bar de meniuri. Orice aplicatie s a a cu interfat grac conttine cel putin o fereastr, cea mai important ind a a a a numit i fereastra principal. as a Constructorii uzuali ai clasei Frame permit crearea unei ferestre cu sau fr titlu, initial invizibil. Pentru ca o fereastr s devin vizibil se va aa a a a a a apela metoda show denit superclasa Window. a n import java.awt.*; public class TestFrame { public static void main(String args[]) { Frame f = new Frame("Titlul ferestrei"); f.show(); } } Crearea ferestrelor prin instantierea direct a obiectelor de tip Frame este a mai putin folosit. De obicei, ferestrele unui program vor denite clase a n separate care extind clasa Frame, ca exemplul de mai jos: n import java.awt.*; class Fereastra extends Frame{ // Constructorul public Fereastra(String titlu) { super(titlu); ... }

234

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

} public class TestFrame { public static void main(String args[]) { Fereastra f = new Fereastra("Titlul ferestrei"); f.show(); } } Gestionarul de pozitionare implicit al clasei Frame este BorderLayout. Din acest motiv, momentul care fereastra este creat dar nici o compon n a nent grac nu este adugat, suprafata de aare a feretrei va determia a a a s nat automota de gestionarul de pozittionare i va oferi doar spatiul necesar a s arii barei ferestrei i grupului de butoane pentru minimizare, maximizare sa s i s nchidere. Acelai efect vom obtine dac o redimenionam i apelm apoi s l a s a metoda pack care determin dimeniunea suprafetei de aare functie de a s n componentele adugate. a Se observ de asemenea c butonul de a a nchidere a ferestrei nu este functional. Tratarea evenimentelor ferestrei se face prin implementarea interfetei WindowListener sau, mai uzual, prin folosirea unui adaptor de tip WindowAdapter. Structura general a unei ferestre este descris de clasa Fereastra din a a exemplul de mai jos: Listing 9.14: Structura general a unei ferestre a
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { // Constructorul public Fereastra ( String titlu ) { super ( titlu ) ; // Tratam evenimentul de inchidere a ferestrei this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { dispose () ; // inchidem fereastra // sau terminam aplicatia System . exit (0) ; } }) ;

9.5. FOLOSIREA FERESTRELOR


// Eventual , schimbam gestionarul de pozitionare setLayout ( new FlowLayout () ) ; // Adaugam componentele pe suprafata ferestrei Button exit = new Button ( " Exit " ) ; add ( exit ) ; // Facem inregistrarea claselor listener exit . addActionListener ( this ) ; // Stabilim dimensiunile pack () ; // implicit // sau explicit // setSize (200 , 200) ; } // Implementam metodele interfetelor de tip listener public void actionPerformed ( ActionEvent e ) { System . exit (0) ; } } public class TestFrame { public static void main ( String args []) { // Cream fereastra Fereastra f = new Fereastra ( " O fereastra " ) ; // O facem vizibila f . show () ; } }

235

Pe lng metodele motenite din clasa Window, exist o serie de metode a a s a specice clasei Frame. Dintre cele mai folosite amintim: getFrames - metod static ce returneaz lista tuturor ferestrelor dea a a schise ale unei aplicatii; setIconImage - seteaz iconita ferestrei; a setMenuBar - seteaz bara de meniuri a ferestrei (vezi Folosirea mea niurilor); setTitle - seteaz titlul ferestrei; a

236

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

setResizable - stabilete dac fereastra poate redimenionat de utis a a lizator;

9.5.3

Clasa Dialog

Toate interfetele grace ofer un tip special de ferestre destinate prelurii a a unor informatii sau a unor date de la utilizator. Acestea se numesc ferestre de dialog sau casete de dialog i sunt implementate prin intermediul clasei s Dialog, subclas direct a clasei Window. a a Diferenta major dintre ferestrele de dialog i ferestrele de tip Frame a s const faptul c o fereastr de dialog este dependent de o alt fereastra a n a a a a (normal sau tot fereastr de dialog), numit i fereastra printe. Cu alte a a as a cuvinte, ferestrele de dialog nu au o existent de sine stttoare. Cnd fera aa a eastra printe este distrus sunt distruse i ferestrele sale de dialog, cnd este a a s a minimizat ferestrele sale de dialog sunt fcute invizibile iar cnd este restaua a a rat acestea sunt aduse la starea care se gseau momentul minimizrii a n a n a ferestrei printe. a Ferestrele de dialog pot de dou tipuri: a modale: care blocheaz accesul la fereastra parinte momentul dea n schiderii lor, cum ar ferestrele de introducere a unor date, de alegere a unui ier, de selectare a unei optiuni, mesaje de avertizare, etc; s nemodale: care nu blocheaz uxul de intrare ctre fereastra printe a a a - de exemplu, dialogul de cutare a unui cuvnt a a ntr-un ier, etc. s Implicit, o fereastr de dialog este nemodal i invizibil, a exist cona as a ns a structori care s specice i aceti parametri. Constructorii clasei Dialog a s s sunt: Dialog(Frame parinte) Dialog(Frame parinte, String titlu) Dialog(Frame parinte, String titlu, boolean modala) Dialog(Frame parinte, boolean modala) Dialog(Dialog parinte) Dialog(Dialog parinte, String titlu) Dialog(Dialog parinte, String titlu, boolean modala)

9.5. FOLOSIREA FERESTRELOR

237

Parametrul printe reprezint referinta la fereastra printe, titlu a a a reprezint titlul ferestrei iar prin argumentul modal specicm dac fera a a a eastra de dialog creat va modal (true) sau nemodal (false - valoarea a a a implicit). a Crearea unei ferestre de dialog este relativ simpla i se realizeaz prin s a derivarea clasei Dialog. Comunicarea dintre fereastra de dialog i fereastra s sa printe, pentru ca aceasta din urm s poat folosi datele introduse (sau a a a a optiunea specicata) caseta de dialog, se poate realiza folosind una din n urmtoarele abordri generale: a a obiectul care reprezint dialogul poate s trateze evenimentele generate a a de componentele de pe suprafata s i s seteze valorile unor variabile as a accesibile ale ferestrei printe momentul care dialogul este a n n ncheiat; obiectul care creeaz dialogul (fereastra printe) s se a a a nregistreze ca asculttor al evenimentelor de la butoanele care determin a a ncheierea dialogului, iar fereastra de dialog s ofere metode publice prin care a datele introduse s e preluate din exterior; a S crem, de exemplu, o fereastr de dialog modal pentru introducerea a a a a unui ir de caractere. Fereastra principal a aplicatiei va printele casetei s a a de dialog, va primi irul de caractere introdus i va modica titlul ca ind s s si acesta. Deschiderea ferestrei de dialog se va face la apsarea unui buton al a ferestrei principale numit Schimba titlul. Cele dou ferestre vor arta ca a a imaginile de mai jos: n Fereastra principal a Fereastra de dialog

Listing 9.15: Folosirea unei ferestre de dialog


import java . awt .*; import java . awt . event .*; // Fereastra principala class FerPrinc extends Frame implements ActionListener {

238

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

public FerPrinc ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new FlowLayout () ) ; setSize (300 , 80) ; Button b = new Button ( " Schimba titlul " ) ; add ( b ) ; b . addActionListener ( this ) ; } public void actionPerformed ( ActionEvent e ) { FerDialog d = new FerDialog ( this , " Dati titlul " , true ) ; String titlu = d . raspuns ; if ( titlu == null ) return ; setTitle ( titlu ) ; } } // Fereastra de dialog class FerDialog extends Dialog implements ActionListener { public String raspuns = null ; private TextField text ; private Button ok , cancel ; public FerDialog ( Frame parinte , String titlu , boolean modala ) { super ( parinte , titlu , modala ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { raspuns = null ; dispose () ; } }) ; text = new TextField ( " " , 30) ; add ( text , BorderLayout . CENTER ) ; Panel panel = new Panel () ; ok = new Button ( " OK " ) ;

9.5. FOLOSIREA FERESTRELOR


cancel = new Button ( " Cancel " ) ; panel . add ( ok ) ; panel . add ( cancel ) ; add ( panel , BorderLayout . SOUTH ) ; pack () ; text . addActionListener ( this ) ; ok . addActionListener ( this ) ; cancel . addActionListener ( this ) ; show () ; } public void actionPerformed ( ActionEvent e ) { Object sursa = e . getSource () ; if ( sursa == ok || sursa == text ) raspuns = text . getText () ; else raspuns = null ; dispose () ; } } // Clasa principala public class TestDialog { public static void main ( String args []) { FerPrinc f = new FerPrinc ( " Fereastra principala " ) ; f . show () ; } }

239

9.5.4

Clasa FileDialog

Pachetul java.awt pune la dispozitie i un tip de fereastr de dialog folosit s a a pentru selectarea unui nume de ier vederea arcrii sau salvrii unui s n nc a a ier: clasa FileDialog, derivat din Dialog. Instantele acestei clase au un s a comportament comun dialogurilor de acest tip de pe majoritatea platformelor de lucru, dar forma care vor aate este specic platformei pe care n s a ruleaz aplicatia. a Constructorii clasei sunt: FileDialog(Frame parinte)

240

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

FileDialog(Frame parinte, String titlu) FileDialog(Frame parinte, String titlu, boolean mod) Parametrul printe reprezint referinta ferestrei printe, titlu reprezint a a a a titlul ferestrei iar prin argumentul mod specicm dac arcm sau a a nc a salvm un ier; valorile pe care le poate lua acest argument sunt: a s FileDialog.LOAD - pentru arcare, respectiv nc FileDialog.SAVE - pentru salvare. // Dialog pentru incarcarea unui fisier new FileDialog(parinte, "Alegere fisier", FileDialog.LOAD); // Dialog pentru salvarea unui fisier new FileDialog(parinte, "Salvare fisier", FileDialog.SAVE); La crearea unui obiect FileDialog acesta nu este implicit vizibil. Dac a aarea sa se face cu show, caseta de dialog va modal. Dac aarea s a a s se face cu setVisible(true), atunci va nemodal. Dup selectarea unui a a ier ea va facut automat invizibil. s a a Pe lng metodele motenite de la superclasa Dialog clasa FileDialog a a s mai contine metode pentru obtinerea numelui ierului sau directorului se s lectat getFile, getDirectory, pentru stabilirea unui criteriu de ltrare setFilenameFilter, etc. S considerm un exemplu care vom alege, prin intermediul unui obiect a a n FileDialog, un ier cu extensia java. Directorul initial este directorul s curent, iar numele implicit este TestFileDialog.java. Numele ierului s ales va aat la consol. s a Listing 9.16: Folosirea unei ferestre de dialog
import java . awt .*; import java . awt . event .*; import java . io .*; class FerPrinc extends Frame implements ActionListener { public FerPrinc ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () {

9.5. FOLOSIREA FERESTRELOR


public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; Button b = new Button ( " Alege fisier " ) ; add (b , BorderLayout . CENTER ) ; pack () ; b . addActionListener ( this ) ; }

241

public void actionPerformed ( ActionEvent e ) { FileDialog fd = new FileDialog ( this , " Alegeti un fisier " , FileDialog . LOAD ) ; // Stabilim directorul curent fd . setDirectory ( " . " ) ; // Stabilim numele implicit fd . setFile ( " TestFileDialog . java " ) ; // Specificam filtrul fd . setFilenameFilter ( new FilenameFilter () { public boolean accept ( File dir , String numeFis ) { return ( numeFis . endsWith ( " . java " ) ) ; } }) ; // Afisam fereastra de dialog ( modala ) fd . show () ; System . out . println ( " Fisierul ales este : " + fd . getFile () ) ; } } public class TestFileDialog { public static void main ( String args []) { FerPrinc f = new FerPrinc ( " Fereastra principala " ) ; f . show () ; } }

Clasa FileDialog este folosit mai rar deoarece Swing exist clasa a n a JFileChooser care ofer mai multe faciliti i prin urmare va constitui a at s prima optiune ntr-o aplicatie cu intefat grac. a a

242

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

9.6

Folosirea meniurilor

Spre deosebire de celelalte obiecte grace care deriv din clasa Component, a componentele unui meniu reprezint instante ale unor clase derivate din sua perclasa abstract MenuComponent. Aceast exceptie este facut deoarece a a a unele platforme grace limiteaz capabilitile unui meniu. a at Meniurile pot grupate dou categorii: n a Meniuri xe (vizibile permanent): sunt grupate ntr-o bar de meniuri a ce contine cte un meniu pentru ecare intrare a sa. La rndul lor, a a aceste meniuri contin articole ce pot selectate, comutatoare sau alte meniuri (submeniuri). O fereastr poate avea un singur meniu x. a Meniuri de context (popup): sunt meniuri invizbile asociate unei ferestre i care se activeaz uzual prin apsarea butonului drept al s a a mouse-ului. O alt diferent fat de meniurile xe const faptul a a a a n c meniurile de context nu sunt grupate a ntr-o bar de meniuri. a In gura de mai jos este pus evident alctuirea unui meniu x: a n a a

Exemplul de mai sus contine o bar de meniuri format din dou meniuri a a a principale File i Edit. Meniul Edit contine la rndul lui alt meniu (submeniu) s a Options, articolul Undo i dou comutatoare Bold i Italic. Prin abuz de s a s limbaj, vom referi uneori bara de meniuri a unei ferestre ca ind meniul ferestrei. In modelul AWT obiectele care reprezint bare de meniuri sunt reprezena tate ca instante al clasei MenuBar. Un obiect de tip MenuBar contine obiecte de tip Menu, care sunt de fapt meniurile derulante propriu-zise. La rndul a lor, acestea pot contine obiecte de tip MenuItem, CheckBoxMenuItem, dar i alte obiecte de tip Menu (submeniuri). s

9.6. FOLOSIREA MENIURILOR

243

Pentru a putea contine un meniu, o component trebuie s implementeze a a interfata MenuContainer. Cel mai adesea, meniurile sunt ataate fere s strelor, mai precis obiectelor de tip Frame, acestea implementnd interfat a a MenuContainer. Ataarea unei bare de meniuri la o fereastr se face prin s a metoda addMenuBar a clasei Frame.

9.6.1

Ierarhia claselor ce descriu meniuri

S vedem continuare care este ierarhia claselor folosite lucrul cu meniuri a n n i s analizm pe rnd aceste clase: s a a a

Clasa MenuComponent este o clasa abstract din care sunt extinse a toate celelalte clase folosite la crearea de meniuri, ind similar celeilalte a superclase abstracte Component. MenuComponent contine metode cu carac ter general, dintre care amintim getName, setName, getFont, setFont, cu sintaxa i semnicatiile uzuale. s

Clasa MenuBar permite crearea barelor de meniuri asociate unei ferestre cadru de tip Frame, adaptnd conceptul de bar de meniuri la platforma a a curent de lucru. Dup cum am mai spus, pentru a lega bara de meniuri la a a o anumit fereastra trebuie apelat metoda setMenuBar din clasa Frame. a a // Crearea barei de meniuri MenuBar mb = new MenuBar();

244

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

// Adaugarea meniurilor derulante la bara de meniuri ... // Atasarea barei de meniuri la o fereastra Frame f = new Frame("Fereastra cu meniu"); f.addMenuBar(mb);

Orice articol al unui meniu trebuie s e o instanta a clasei Menua Item. Obiectele acestei clase descriu aadar optiunile individuale ale mes niurilor derulante, cum sunt Open, Close, Exit, etc. O instant a a clasei MenuItem reprezint de fapt un buton sau un comutator, cu o anumit a a etichet care va aprea meniu, it eventual de un accelerator (obiect a a n nsot a de tip MenuShortcut) ce reprezint combinatia de taste cu care articolul a poate apelat rapid (vezi Acceleratori).

Clasa Menu permite crearea unui meniu derulant ntr-o bar de meniuri. a Optional, un meniu poate declarat ca ind tear-o, ceea ce nseamn c a a poate deschis i deplasat cu mouse-ul (dragged) s ntr-o alt pozitie dect a a cea original (rupt din pozitia sa). Acest mecanism este dependent de a platform i poate ignorat pe unele dintre ele. Fiecare meniu are o etichet, as a care este de fapt numele su ce va aat pe bara de meniuri. Articolele a s dintr-un meniu trebuie s apartin clasei MenuItem, ceea ce a a nseamn c pot a a instante ale uneia din clasele MenuItem, Menu sau CheckboxMenuItem.

Clasa CheckboxMenuItem implementeaz a ntr-un meniu articole de tip comutator - care au dou stri logice (validat/nevalidat), actionarea ara a ticolului determinnd trecerea sa dintr-o stare alta. La validarea unui a n comutator dreptul etichetei sale va aat un simbol grac care indic n s a acest lucru; la invalidarea sa, simbolul grac respectiv va disprea. Clasa a CheckboxMenuItem are aceeai functionalitate cu cea a casetelor de validare s de tip Checkbox, ambele implementnd interfata ItemSelectable. a

9.6. FOLOSIREA MENIURILOR

245

S vedem continuare cum ar arta un program care construiete un a n a s meniu ca gura prezentat anterior: n a

Listing 9.17: Crearea unui meniu


import java . awt .*; import java . awt . event .*; public class TestMenu { public static void main ( String args []) { Frame f = new Frame ( " Test Menu " ) ; MenuBar mb = new MenuBar () ; Menu fisier = new Menu ( " File " ) ; fisier . add ( new MenuItem ( " Open " ) ) ; fisier . add ( new MenuItem ( " Close " ) ) ; fisier . addSeparator () ; fisier . add ( new MenuItem ( " Exit " ) ) ; Menu optiuni = new Menu ( " Options " ) ; optiuni . add ( new MenuItem ( " Copy " ) ) ; optiuni . add ( new MenuItem ( " Cut " ) ) ; optiuni . add ( new MenuItem ( " Paste " ) ) ; Menu editare = new Menu ( " Edit " ) ; editare . add ( new MenuItem ( " Undo " ) ) ; editare . add ( optiuni ) ; editare . addSeparator () ; editare . add ( new CheckboxMenuItem ( " Bold " ) ) ; editare . add ( new CheckboxMenuItem ( " Italic " ) ) ; mb . add ( fisier ) ; mb . add ( editare ) ; f . setMenuBar ( mb ) ; f . setSize (200 , 100) ; f . show () ; } }

246

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

9.6.2

Tratarea evenimentelor generate de meniuri

La alegerea unei optiuni dintr-un meniu se genereaz e un eveniment de tip a ActionEvent dac articolul respectiv este de tip MenuItem, e ItemEvent a pentru comutatoarele CheckboxMenuItem. Aadar, pentru a activa optiunile s unui meniu trebuie implementate interfatele ActionListener sau/i Item s Listener cadrul obiectelor care trebuie s specice codul ce va executat n a la alegerea unei optiuni i implementate metodele actionPerformed, respec s tiv itemStatChanged. Fiecrui meniu putem asocia un obiect receptor a i diferit, ceea ce uureaz munca cazul care ierarhia de meniuri este coms a n n plex. Pentru a realiza legtura a a ntre obiectul meniu i obiectul de tip listener s trebuie s adaugm receptorul lista de asculttori a meniului respectiv, a a n a ntocmai ca pe orice component, folosind metodele addActionListener, a respectiv addItemListener. Aadar, tratarea evenimentelor generate de obiecte de tip MenuItem este s identic cu tratarea butoanelor, ceea ce face posibil ca unui buton de pe a suprafata de aare s corespund o optiune dintr-un meniu, ambele cu s a i a acelai nume, tratarea evenimentului corespunztor apsarii butonului, sau s a a alegerii optiunii, fcndu-se o singur dat a a a a ntr-o clas care este a nregistrat a ca receptor att la buton ct i la meniu. a a s Obiectele de tip CheckboxMenuItem tip se gsesc a ntr-o categorie comun a cu List, Choice, CheckBox, toate implementnd interfata ItemSelectable a i deci tratarea lor va fcut la fel. Tipul de operatie selectare / deselectare s a a este codicat evenimentul generat de cmpurile statice ItemEvent.SELECTED n a i ItemEvent.DESELECTED. s Listing 9.18: Tratarea evenimentelor unui meniu
import java . awt .*; import java . awt . event .*; public class TestMenuEvent extends Frame implements ActionListener , ItemListener { public TestMenuEvent ( String titlu ) { super ( titlu ) ; MenuBar mb = new MenuBar () ; Menu test = new Menu ( " Test " ) ; CheckboxMenuItem check = new CheckboxMenuItem ( " Check me " ) ;

9.6. FOLOSIREA MENIURILOR


test . add ( check ) ; test . addSeparator () ; test . add ( new MenuItem ( " Exit " ) ) ; mb . add ( test ) ; setMenuBar ( mb ) ; Button btnExit = new Button ( " Exit " ) ; add ( btnExit , BorderLayout . SOUTH ) ; setSize (300 , 200) ; show () ; test . addActionListener ( this ) ; check . addItemListener ( this ) ; btnExit . addActionListener ( this ) ; } public void actionPerformed ( ActionEvent e ) { // Valabila si pentru meniu si pentru buton String command = e . getActionCommand () ; if ( command . equals ( " Exit " ) ) System . exit (0) ; } public void itemStateChanged ( ItemEvent e ) { if ( e . getStateChange () == ItemEvent . SELECTED ) setTitle ( " Checked ! " ) ; else setTitle ( " Not checked ! " ) ; } public static void main ( String args []) { TestMenuEvent f = new TestMenuEvent ( " Tratare evenimente meniuri " ) ; f . show () ; } }

247

9.6.3

Meniuri de context (popup)

Au fost introduse ncepnd cu AWT 1.1 i sunt implementate prin intermediul a s clasei PopupMenu, subclas direct a clasei Menu. Sunt meniuri invizibile a a care sunt activate uzual prin apsarea butonului drept al mouse-ului, ind a

248

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

aate la pozitia la care se gsea mouse-ul momentul apsrii butonului s a n aa su drept. Metodele de adugare a articolelor unui meniu de context sunt a a motenite s ntocmai de la meniurile xe. PopupMenu popup = new PopupMenu("Options"); popup.add(new MenuItem("New")); popup.add(new MenuItem("Edit")); popup.addSeparator(); popup.add(new MenuItem("Exit")); Aarea meniului de context se face prin metoda show: s popup.show(Component origine, int x, int y) i este de obicei rezultatul apsarii unui buton al mouse-ului, pentru a avea s a acces rapid la meniu. Argumentul origine reprezint componenta fat de a a originile creia se va calcula pozitia de aare a meniului popup. De obicei, a s reprezint instanta ferestrei care se va aa meniul. Deoarece interactiunea a n s cu mouse-ul este dependent de platforma de lucru, exist o metod care a a a determin dac un eveniment de tip MouseEvent poate responsabil cu a a deschiderea unui meniu de context. Aceasta este isPopupTrigger i este s denit clasa MouseEvent. Pozitionarea i aarea meniului este a a n s s ns responsabilitatea programatorului. Meniurile de context nu se adaug la un alt meniu (bar sau sub-meniu) a a ci se ataeaz la o component (de obicei la o fereastr) prin metoda add s a a a a acesteia. In cazul cnd avem mai multe meniuri popup pe care vrem s a a le folosim ntr-o fereastr, trebuie s le denim pe toate i, la un moment a a s dat, vom aduga ferestrei meniul corespunztor dup care vom face vizibil. a a a l Dup a nchiderea acestuia, vom rupe legtura a ntre fereastr i meniu prin as instructiunea remove: fereastra.add(popup1); ... fereastra.remove(popup1); fereastra.add(popup2); In exemplul de mai jos, vom crea un meniu de contex pe care vom activa l la apsarea butonului drept al mouse-ului pe suprafata ferestrei principale. a Tratarea evenimentelor generate de un meniu popup se realizeaz identic ca a pentru meniurile xe.

9.6. FOLOSIREA MENIURILOR Listing 9.19: Folosirea unui meniu de context (popup)
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { // Definim meniul popup al ferestrei private PopupMenu popup ; // Pozitia meniului va fi relativa la fereastra private Component origin ; public Fereastra ( String titlu ) { super ( titlu ) ; origin = this ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; this . addMouseListener ( new MouseAdapter () { public void mousePressed ( MouseEvent e ) { if ( e . isPopupTrigger () ) popup . show ( origin , e . getX () , e . getY () ) ; } public void mouseReleased ( MouseEvent e ) { if ( e . isPopupTrigger () ) popup . show ( origin , e . getX () , e . getY () ) ; } }) ; setSize (300 , 300) ; // Cream meniul popup popup = new PopupMenu ( " Options " ) ; popup . add ( new MenuItem ( " New " ) ) ; popup . add ( new MenuItem ( " Edit " ) ) ; popup . addSeparator () ; popup . add ( new MenuItem ( " Exit " ) ) ; add ( popup ) ; // atasam meniul popup ferestrei popup . addActionListener ( this ) ; } public void actionPerformed ( ActionEvent e ) { String command = e . getActionCommand () ; if ( command . equals ( " Exit " ) ) System . exit (0) ;

249

250
} }

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

public class TestPopupMenu { public static void main ( String args []) { Fereastra f = new Fereastra ( " PopupMenu " ) ; f . show () ; } }

9.6.4

Acceleratori (Clasa MenuShortcut)

Pentru articolele unui menu este posibil specicarea unor combinatii de a taste numite acceleratori (shortcuts) care s permit accesarea direct, prin a a a intermediul tastaturii, a optiunilor dintr-un meniu. Astfel, oricrui obiect a de tip MenuItem poate asociat un obiect de tip accelerator, denit prin i intermediul clasei MenuShortcut. Singurele combinatii de taste care pot juca rolul acceleratorilor sunt: Ctrl + Tasta sau Ctrl + Shift + Tasta. Atribuirea unui accelerator la un articol al unui meniu poate realizat prin a constructorul obiectelor de tip MenuItem forma: n MenuItem(String eticheta, MenuShortcut accelerator), ca exemplele n de mai jos: // Ctrl+O new MenuItem("Open", new MenuShortcut(KeyEvent.VK_O)); // Ctrl+P new MenuItem("Print", new MenuShortcut(p)); // Ctrl+Shift+P new MenuItem("Preview", new MenuShortcut(p), true);

9.7

Folosirea componentelor AWT

In continuare vor date exemple de folosire ale componentelor AWT, care n s e puse evidentt ct mai multe din particularitile acestora, precum a n a a at i modul de tratare a evenimentelor generate. s

9.7. FOLOSIREA COMPONENTELOR AWT

251

9.7.1

Clasa Label

Un obiect de tip Label (etichet) reprezint o component pentru plasarea a a a unui text pe o suprafata de aare. O etichet este format dintr-o singur s a a a linie de text static ce nu poate modicat de ctre utilizator, dar poate a modicat din program.

Listing 9.20: Folosirea clasei Label


import java . awt .*; public class TestLabel { public static void main ( String args []) { Frame f = new Frame ( " Label " ) ; Label nord , sud , est , vest , centru ; nord = new Label ( " Nord " , Label . CENTER ) ; nord . setForeground ( Color . blue ) ; sud = new Label ( " Sud " , Label . CENTER ) ; sud . setForeground ( Color . red ) ; vest = new Label ( " Vest " , Label . LEFT ) ; vest . setFont ( new Font ( " Dialog " , Font . ITALIC , 14) ) ; est = new Label ( " Est " , Label . RIGHT ) ; est . setFont ( new Font ( " Dialog " , Font . ITALIC , 14) ) ; centru = new Label ( " Centru " , Label . CENTER ) ; centru . setBackground ( Color . yellow ) ; centru . setFont ( new Font ( " Arial " , Font . BOLD , 20) ) ; f . add ( nord , BorderLayout . NORTH ) ; f . add ( sud , BorderLayout . SOUTH ) ; f . add ( est , BorderLayout . EAST ) ; f . add ( vest , BorderLayout . WEST ) ;

252

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL


f . add ( centru , BorderLayout . CENTER ) ; f . pack () ; f . show () ;

} }

9.7.2

Clasa Button

Un obiect de tip Button reprezint o component pentru plasarea unui bua a ton etichetat pe o suprafata de aare. Textul etichetei este format dintr-o s singur linie. a

Listing 9.21: Folosirea clasei Button


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( null ) ; setSize (200 , 120) ; Button b1 = new Button ( " OK " ) ; b1 . setBounds (30 , 30 , 50 , 70) ; b1 . setFont ( new Font ( " Arial " , Font . BOLD , 14) ) ;

9.7. FOLOSIREA COMPONENTELOR AWT


b1 . setBackground ( Color . orange ) ; add ( b1 ) ; Button b2 = new Button ( " Cancel " ) ; b2 . setBounds (100 , 30 , 70 , 50) ; b2 . setForeground ( Color . blue ) ; add ( b2 ) ; b1 . addActionListener ( this ) ; b2 . addActionListener ( this ) ; } // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { String command = e . getActionCommand () ; System . out . println ( e ) ; if ( command . equals ( " OK " ) ) setTitle ( " Confirmare ! " ) ; else if ( command . equals ( " Cancel " ) ) setTitle ( " Anulare ! " ) ; } } public class TestButton { public static void main ( String args []) { Fereastra f = new Fereastra ( " Button " ) ; f . show () ; } }

253

9.7.3

Clasa Checkbox

Un obiect de tip Checkbox (comutator) reprezint o component care se a a poate gsi dou stri: selectat sau neselectat (on/o). Actiunea a n a a a a utilizatorului asupra unui comutator trece pe acesta starea complemenl n tar celei care se gsea. Este folosit pentru a prelua o anumit optiune de a n a a la utilizator.

254

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Listing 9.22: Folosirea clasei Checkbox


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label1 , label2 ; private Checkbox cbx1 , cbx2 , cbx3 ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (5 , 1) ) ; label1 = new Label ( " Ingrediente Pizza : " , Label . CENTER ) ; label1 . setBackground ( Color . orange ) ; label2 = new Label ( " " ) ; label2 . setBackground ( Color . lightGray ) ; cbx1 = new Checkbox ( " cascaval " ) ; cbx2 = new Checkbox ( " sunca " ) ; cbx3 = new Checkbox ( " ardei " ) ; add ( label1 ) ; add ( label2 ) ; add ( cbx1 ) ; add ( cbx2 ) ; add ( cbx3 ) ;

9.7. FOLOSIREA COMPONENTELOR AWT

255

setSize (200 , 200) ; cbx1 . addItemListener ( this ) ; cbx2 . addItemListener ( this ) ; cbx3 . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { StringBuffer ingrediente = new StringBuffer () ; if ( cbx1 . getState () == true ) ingrediente . append ( " cascaval " ) ; if ( cbx2 . getState () == true ) ingrediente . append ( " sunca " ) ; if ( cbx3 . getState () == true ) ingrediente . append ( " ardei " ) ; label2 . setText ( ingrediente . toString () ) ; } } public class TestCheckbox { public static void main ( String args []) { Fereastra f = new Fereastra ( " Checkbox " ) ; f . show () ; } }

9.7.4

Clasa CheckboxGroup

Un obiect de tip CheckboxGroup denete un grup de comutatoare din care s doar unul poate selectat. Uzual, aceste componente se mai numesc butoane radio. Aceast clas nu este derivat din Component, oferind doar o a a a modalitate de grupare a componentelor de tip Checkbox.

256

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Listing 9.23: Folosirea clasei CheckboxGroup


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label1 , label2 ; private Checkbox cbx1 , cbx2 , cbx3 ; private CheckboxGroup cbg ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (5 , 1) ) ; label1 = new Label ( " Alegeti postul TV " , Label . CENTER ) ; label1 . setBackground ( Color . orange ) ; label2 = new Label ( " " , Label . CENTER ) ; label2 . setBackground ( Color . lightGray ) ; cbg = new CheckboxGroup () ; cbx1 = new Checkbox ( " HBO " , cbg , false ) ; cbx2 = new Checkbox ( " Discovery " , cbg , false ) ; cbx3 = new Checkbox ( " MTV " , cbg , false ) ; add ( label1 ) ; add ( label2 ) ; add ( cbx1 ) ;

9.7. FOLOSIREA COMPONENTELOR AWT


add ( cbx2 ) ; add ( cbx3 ) ; setSize (200 , 200) ; cbx1 . addItemListener ( this ) ; cbx2 . addItemListener ( this ) ; cbx3 . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { Checkbox cbx = cbg . getS e lec ted Che ck box () ; if ( cbx != null ) label2 . setText ( cbx . getLabel () ) ; } } public class TestCheckboxGroup { public static void main ( String args []) { Fereastra f = new Fereastra ( " CheckboxGroup " ) ; f . show () ; } }

257

9.7.5

Clasa Choice

Un obiect de tip Choice denete o list de optiuni din care utilizatorul s a poate selecta una singur. La un moment dat, din a ntreaga list doar o sina gur optiune este vizibil, cea selectat momentul curent. O component a a a n a Choice este it de un buton etichetat cu o sageat vertical la apsarea nsot a a a a cruia este aat a s a ntreaga sa list de elemente, pentru ca utilizatorul s a a poat selecta o anumit optiune. a a

258

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL Listing 9.24: Folosirea clasei Choice

import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label ; private Choice culori ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (4 , 1) ) ; label = new Label ( " Alegeti culoarea " ) ; label . setBackground ( Color . red ) ; culori = new Choice () ; culori . add ( " Rosu " ) ; culori . add ( " Verde " ) ; culori . add ( " Albastru " ) ; culori . select ( " Rosu " ) ; add ( label ) ; add ( culori ) ; setSize (200 , 100) ; culori . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { switch ( culori . getSelectedIndex () ) { case 0: label . setBackground ( Color . red ) ; break ; case 1: label . setBackground ( Color . green ) ; break ; case 2: label . setBackground ( Color . blue ) ;

9.7. FOLOSIREA COMPONENTELOR AWT


} } } public class TestChoice { public static void main ( String args []) { Fereastra f = new Fereastra ( " Choice " ) ; f . show () ; } }

259

9.7.6

Clasa List

Un obiect de tip List denete o list de optiuni care poate setat astfel s a a at utilizatorul s poat selecta o singur optiune sau mai multe. Toate nc a a a elementele listei sunt vizibile limita dimensiunilor grace ale componentei. n

Listing 9.25: Folosirea clasei List


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label ; private List culori ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () {

260

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL


public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (2 , 1) ) ; label = new Label ( " Alegeti culoarea " , Label . CENTER ) ; label . setBackground ( Color . red ) ; culori = new List (3) ; culori . add ( " Rosu " ) ; culori . add ( " Verde " ) ; culori . add ( " Albastru " ) ; culori . select (3) ; add ( label ) ; add ( culori ) ; setSize (200 , 200) ; culori . addItemListener ( this ) ;

} // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { switch ( culori . getSelectedIndex () ) { case 0: label . setBackground ( Color . red ) ; break ; case 1: label . setBackground ( Color . green ) ; break ; case 2: label . setBackground ( Color . blue ) ; } } } public class TestList { public static void main ( String args []) { Fereastra f = new Fereastra ( " List " ) ; f . show () ; } }

9.7. FOLOSIREA COMPONENTELOR AWT

261

9.7.7

Clasa ScrollBar

Un obiect de tip Scrollbar denete o bar de delare pe vertical sau s a a orizontal. Este util pentru punerea la dispozitia utilizatorului a unei a a modaliti sugestive de a alege o anumit valoare dintr-un interval. at a

Listing 9.26: Folosirea clasei ScrollBar


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements Ad ju st me nt Lis te ne r { private Scrollbar scroll ; private Label valoare ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (2 , 1) ) ; valoare = new Label ( " " , Label . CENTER ) ; valoare . setBackground ( Color . lightGray ) ; scroll = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 101) ; add ( valoare ) ; add ( scroll ) ; setSize (200 , 80) ; scroll . add Adjus tmen tLis t e n e r ( this ) ; } // Metoda interfetei Adju st me ntL is te ne r

262

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

public void ad ju st m e n t V a lu eC ha n g e d ( AdjustmentEvent e ) { valoare . setText ( scroll . getValue () + " % " ) ; } } public class TestScrollbar { public static void main ( String args []) { Fereastra f = new Fereastra ( " Scrollbar " ) ; f . show () ; } }

9.7.8

Clasa ScrollPane

Un obiect de tip ScrollPane permite ataarea unor bare de delare (oris zontal si/sau vertical) oricrei componente grace. Acest lucru este util as a a pentru acele componente care nu au implementat functionalitatea de dea lare automat, cum ar listele (obiecte din clasa List). a

Listing 9.27: Folosirea clasei ScrollPane


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame { private ScrollPane sp ; private List list ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ;

9.7. FOLOSIREA COMPONENTELOR AWT


} }) ; list = new List (7) ; list . add ( " Luni " ) ; list . add ( " Marti " ) ; list . add ( " Miercuri " ) ; list . add ( " Joi " ) ; list . add ( " Vineri " ) ; list . add ( " Sambata " ) ; list . add ( " Duminica " ) ; list . select (1) ; sp = new ScrollPane ( ScrollPane . SC ROLLBAR S_ALWAY S ) ; sp . add ( list ) ; add ( sp , BorderLayout . CENTER ) ; setSize (200 , 200) ; } } public class TestScrollPane { public static void main ( String args []) { Fereastra f = new Fereastra ( " ScrollPane " ) ; f . show () ; } }

263

9.7.9

Clasa TextField

Un obiect de tip TextField denete un control de editare a textului pe o s singur linie. Este util pentru interogarea utilizatorului asupra unor valori. a

264

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL Listing 9.28: Folosirea clasei TextField

import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements TextListener { private TextField nume , parola ; private Label acces ; private static final String UID = " Duke " , PWD = " java " ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (3 , 1) ) ; setBackground ( Color . lightGray ) ; nume = new TextField ( " " , 30) ; parola = new TextField ( " " , 10) ; parola . setEchoChar ( * ) ; Panel p1 = new Panel () ; p1 . setLayout ( new FlowLayout ( FlowLayout . LEFT ) ) ; p1 . add ( new Label ( " Nume : " ) ) ; p1 . add ( nume ) ; Panel p2 = new Panel () ; p2 . setLayout ( new FlowLayout ( FlowLayout . LEFT ) ) ; p2 . add ( new Label ( " Parola : " ) ) ; p2 . add ( parola ) ; acces = new Label ( " Introduceti numele si parola ! " , Label . CENTER ) ; add ( p1 ) ; add ( p2 ) ; add ( acces ) ; setSize (350 , 100) ; nume . addTextListener ( this ) ; parola . addTextListener ( this ) ; }

9.7. FOLOSIREA COMPONENTELOR AWT

265

// Metoda interfetei TextListener public void textValueChanged ( TextEvent e ) { if ( nume . getText () . length () == 0 || parola . getText () . length () == 0) { acces . setText ( " " ) ; return ; } if ( nume . getText () . equals ( UID ) && parola . getText () . equals ( PWD ) ) acces . setText ( " Acces permis ! " ) ; else acces . setText ( " Acces interzis ! " ) ; } } public class TestTextField { public static void main ( String args []) { Fereastra f = new Fereastra ( " TextField " ) ; f . show () ; } }

9.7.10

Clasa TextArea

Un obiect de tip TextArea denete un control de editare a textului pe mai s multe linii. Este util pentru editarea de texte, introducerea unor comentarii, etc .

Listing 9.29: Folosirea clasei TextArea


import java . awt .*;

266

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

import java . awt . event .*; import java . io .*; class Fereastra extends Frame implements TextListener , ActionListener { private TextArea text ; private TextField nume ; private Button salvare ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setBackground ( Color . lightGray ) ; text = new TextArea ( " " , 30 , 10 , TextArea . S C RO L L BA R S _ VE R T I C A L _ O N L Y ) ; nume = new TextField ( " " , 12) ; salvare = new Button ( " Salveaza text " ) ; salvare . setEnabled ( false ) ; Panel fisier = new Panel () ; fisier . add ( new Label ( " Fisier : " ) ) ; fisier . add ( nume ) ; add ( fisier , BorderLayout . NORTH ) ; add ( text , BorderLayout . CENTER ) ; add ( salvare , BorderLayout . SOUTH ) ; setSize (300 , 200) ; text . addTextListener ( this ) ; salvare . addActionLi stener ( this ) ; } // Metoda interfetei TextListener public void textValueChanged ( TextEvent e ) { if ( text . getText () . length () == 0 || nume . getText () . length () == 0) salvare . setEnabled ( false ) ; else salvare . setEnabled ( true ) ;

9.7. FOLOSIREA COMPONENTELOR AWT


} // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { String continut = text . getText () ; try { PrintWriter out = new PrintWriter ( new FileWriter ( nume . getText () ) ) ; out . print ( continut ) ; out . close () ; text . requestFocus () ; } catch ( IOException ex ) { ex . printStackTrace () ; } } } public class TestTextArea { public static void main ( String args []) { Fereastra f = new Fereastra ( " TextArea " ) ; f . show () ; } }

267

268

CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL

Capitolul 10 Desenarea
10.1 Conceptul de desenare

Un program Java care are interfat grac cu utilizatorul trebuie s deseneze a a a pe ecran toate componentele sale care au o reprezentare vizual. Aceast a a desenare include componentele standard folosite aplicatie precum i cele n s denite de ctre programator. Desenarea componentelor se face automat i a s este un proces care se execut urmtoarele situatii: a n a la aarea pentru prima dat a unei componente; s a la operatii de minimizare, maximizare, redimensionare a suprafetei de aare; s ca rspuns al unei solicitri explicite a programului. a a Metodele care controleaz procesul de desenare se gsesc clasa Component a a n i sunt urmtoarele: s a void paint(Graphics g) - Deseneaz o component. Este o metod a a a supradenit de ecare component parte pentru a furniza reprezentarea a a n sa grac specic. Metoda este apelat de ecare dat cnd continutul a a a a a componentei trebuie desenat sau redesenat i nu va apelat explicit. s a void update(Graphics g) - Actualizeaz starea grac a unei coma a ponente. Actiunea acestei metode se realizeaz trei pai: a n s 1. terge componenta prin supradesenarea ei cu culoarea fundalului; s 269

270

CAPITOLUL 10. DESENAREA 2. stabilete culoarea (foreground) a componentei; s 3. apeleaz metoda paint pentru a redesena componenta. a

void repaint() - Execut explicit un apel al metodei update pentru a a actualiza reprezentarea grac a unei componente. a Dup cum se observ, singurul argument al metodelor paint i update a a s este un obiect de tip Graphics. Acesta reprezint contextul grac care se a n execut desenarea componentelor (vezi Contextul grac de desenare - clasa a Graphics).

Atentie Toate desenele care trebuie s apar pe o suprafata de desenare se reala a izeaz metoda paint a unei componente, general apelat automat sau a n n a explicit cu metoda repaint ori de cte ori componenta respectiv trebuie a a redesenat. Exist posibilitatea de a desena i afara metodei paint, a a a s n ns aceste desene se vor pierde la prima operatie de minimizare, maximizare, redimensionare a suprafetei de aare. s

10.1.1

Metoda paint

Dup cum am spus, toate desenele care trebuie s apar pe o suprafat de a a a a aare se realizeaz metoda paint a unei componente. Metoda paint este s a n denit superclasa Component a nu are nici o implementare i, din acest a n ns s motiv, orice obiect grac care dorete s se deseneze trebuie s o suprades a a neasc pentru a-i crea propria sa reprezentare. Componentele standard a s AWT au deja supradenit aceast metod deci nu trebuie s ne preocupe a a a a desenarea lor, a putem modica reprezentarea lor grac prin crearea ns a unei subclase i supradenirea metodei paint, avnd a grij s apelm i s a ns a a a s metoda superclasei care se ocup cu desenarea efectiv a componentei. a a In exemplul de mai jos, redenim metoda paint pentru un obiect de tip Frame, pentru a crea o clas ce instantiaz ferestre pentru o aplicatie a a demonstrativ ( coltul stnga sus este aat textul Aplicatie DEMO). a n a s

10.1. CONCEPTUL DE DESENARE Listing 10.1: Supradenirea metodei paint


import java . awt .*; class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (200 , 100) ; } public void paint ( Graphics g ) { // Apelam metoda paint a clasei Frame super . paint ( g ) ; g . setFont ( new Font ( " Arial " , Font . BOLD , 11) ) ; g . setColor ( Color . red ) ; g . drawString ( " Aplicatie DEMO " , 5 , 35) ; } } public class TestPaint { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test paint " ) ; f . show () ; } }

271

Observati c la orice redimensionare a ferestrei textul Aplicatie DEMO a va redesenat. Dac desenarea acestui text ar fost facut oriunde alt a a n a parte dect metoda paint, la prima redimensionare a ferestrei acesta s-ar a n pierde. Aadar, desenarea Java trebuie s se fac doar cadrul metodelor s n a a n paint ale componentelor grace.

10.1.2

Suprafete de desenare - clasa Canvas

In afara posibilitii de a utiliza componente grace standard, Java ofer at a i posibilitatea controlului la nivel de punct (pixel) pe dispozitivul grac, s respectiv desenarea a diferite forme grace direct pe suprafata unei compo nente. Dei este posibil, general nu se deseneaz la nivel de pixel direct s n a pe suprafata ferestrelor sau a altor containere, ci vor folosite clase dedicate acestui scop. In AWT a fost denit un tip special de component numit Canvas a a (pnz de pictor), al crei scop este de a extins pentru a implementa a a a a

272

CAPITOLUL 10. DESENAREA

obiecte grace cu o anumit aiare. Aadar, Canvas este o clas generic a nft s s a a din care se deriveaz subclase pentru crearea suprafetelor de desenare (plane). a s Planele nu pot contine alte componente grace, ele ind utilizate doar ca s suprafete de desenat sau ca fundal pentru animatie. Desenarea pe o plana s se face prin supradenirea metodei paint a acesteia. Concret, o plan este o suprafat dreptunghiular de culoare alb, pe sa a a a care se poate desena. Dimensiunile sale implicite sunt 0 i, din acest mos tiv, este recomandat ca o plana s redeneasca metoda getPreferredSize, s a eventual i getMinimumSize, getMaximumSize, deoarece acestea vor apelate s de ctre gestionarii de pozitionare. a Etapele uzuale care trebuie parcurse pentru crearea unui desen, sau mai bine zis a unei componente cu o anumit aiare, sunt: a nft s crearea unei plane de desenare, adic o subclas a lui Canvas; s a a redenirea metodei paint din clasa respectiv; a redenirea metodelor getPreferredSize, eventual getMinimumSize, getMaximumSize; adugarea planei pe un container cu metoda add. a s tratarea evenimentelor de tip FocusEvent, KeyEvent, MouseEvent, ComponentEvent, dac este cazul. a Denirea generic a unei plane are urmtorul format: a s a class Plansa extends Canvas implements ...Listener { //Eventual, unul sau mai multi constructori public Plansa() { ... } // Metode de desenare a componentei public void paint(Graphics g) { ... } // Metodele folosite de gestionarii de pozitionare public Dimension getPreferredSize() { // Dimensiunea implicita a plansei

10.1. CONCEPTUL DE DESENARE return ...; } public Dimension getMinimumSize() { return ... } public Dimension getMaximumSize() { return ... } // Implementarea metodelor interfetelor de tip Listener ... }

273

S denim o plan pe care desenm un ptrat i cercul su circumscris, a sa a a s a colorate diferite. La ecare click de mouse, vom interschimba cele dou culori a ntre ele. Listing 10.2: Folosirea clasei Canvas
import java . awt .*; import java . awt . event .*; class Plansa extends Canvas { Dimension dim = new Dimension (100 , 100) ; private Color color [] = { Color . red , Color . blue }; private int index = 0; public Plansa () { this . addMouseListener ( new MouseAdapter () { public void mouseClicked ( MouseEvent e ) { index = 1 - index ; repaint () ; } }) ; } public void paint ( Graphics g ) { g . setColor ( color [ index ]) ; g . drawRect (0 , 0 , dim . width , dim . height ) ; g . setColor ( color [1 - index ]) ; g . fillOval (0 , 0 , dim . width , dim . height ) ; } public Dimension getPreferredSize () {

274
return dim ; } }

CAPITOLUL 10. DESENAREA

class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (200 , 200) ; add ( new Plansa () , BorderLayout . CENTER ) ; } } public class TestCanvas { public static void main ( String args []) { new Fereastra ( " Test Canvas " ) . show () ; } }

10.2

Contextul grac de desenare

Inainte ca utilizatorul s poat desena, el trebuie s obtin un context grac a a a a de desenare pentru suprafata creia apartine regiunea pe care se va desena. a i Un context grac este, de fapt, un obiect prin intermediul cruia putem a controla procesul de desenare a unui obiect. In general, desenarea se poate face: pe o portiune de ecran, la imprimant sau a ntr-o zon virtual de memorie. a a Un context grac este specicat prin intermediul unui obiect de tip Graphics primit ca parametru metodele paint i update. In functie de dispozin s tivul zic pe care se face aarea (ecran, imprimant, plotter, etc) metodele s a de desenare au implementri interne diferite, transparente utilizatorului. a Clasa Graphics pune la dispozitie metode pentru: primitive grace: desenarea de guri geometrice, texte i imagini s stabilirea proprietilor contextului grac, adic stabilirea: at a

10.2. CONTEXTUL GRAFIC DE DESENARE culorii i fontului curente cu care se face desenarea, s originii coordonatelor suprafetei de desenare, suprafetei care sunt vizibile componentelor desenate, n modului de desenare.

275

10.2.1

Proprietile contextului grac at

La orice tip de desenare parametrii legati de culoare, font, etc. vor specicati pentru contextul grac care se face desenarea i nu vor trimii ca arn s s gumente metodelor respective de desenare. In continuare, enumerm aceste a proprieti i metodele asociate lor din clasa Graphics. at s Metode Color getColor() void setColor(Color c) Fontul de scriere a textelor Font getFont() void setFont(Font f) Originea coordonatelor translate(int x, int y) Zona de decupare Shape getClip() (zona care sunt vizibile desenele) void setClip(Shape s) n Modul de desenare void setXorMode(Color c) void setPaintMode(Color c) Proprietate Culoarea de desenare

10.2.2

Primitive grace

Prin primitive grace ne vom referi continuare la metodele clasei Graphics, n care permit desenarea de guri geometrice i texte. s Desenarea textelor se face cu uzual cu metoda drawString care primete s ca argumente un ir i coltul din stnga-jos al textului. Textul va desenat s s a cu fontul i culoarea curente ale contextului grac. s // Desenam la coordonatele x=10, y=20; drawString("Hello", 10, 20); Desenarea gurilor geometrice se realizeaz cu urmtoarele metode: a a

276 Figur geometric a a Linie

CAPITOLUL 10. DESENAREA Metode drawLine drawPolyline Dreptunghi simplu drawRect fillRect clearRect Dreptunghi cu chenar draw3DRect ridicat sau adncit fill3DRect a Dreptunghi cu colturi drawRoundRect retunjite fillRoundRect Poligon drawPolygon fillPolygon Oval (Elips a drawOval fillOval Arc circular sau drawArc eliptic fillArc

Metodele care ncep cu ll vor desena guri geometrice care au interiorul colorat, adic umplut cu culoarea curent a contextului de desenare, a a timp ce metodele care n ncep cu draw vor desena doar conturul gurii respective.

10.3

Folosirea fonturilor

Dup cum vazut, pentru a scrie un text pe ecran avem dou posibiliti. a a at Prima dintre acestea este s folosim o component orientat-text, cum ar a a a Label, iar a doua s apelm la metodele clasei Graphics de desenare a a a textelor, cum ar drawString. Indiferent de modalitatea aleas, putem a specica prin intermediul fonturilor cum s arate textul respectiv, acest lucru a realizndu-se prin metoda setFont e din clasa Component, e din Graphics. a Cei mai importanti parametri ce caracterizeaz un font sunt: a Numele fontului: Helvetica Bold, Arial Bold Italic, etc. Familia din care face parte fontul: Helvetica, Arial, etc. Dimensiunea fontului: altimea sa; n Stilul fontului: ngroat (bold), s nclinat (italic);

10.3. FOLOSIREA FONTURILOR Metrica fontului.

277

Clasele care ofer suport pentru lucrul cu fonturi sunt Font i FontMetrics, a s continuare ind prezentate modalitile de lucru cu acestea. n at

10.3.1

Clasa Font

Un obiect de tip Font ncapsuleaz informatii despre toti parametrii unui a font, mai putin despre metrica acestuia. Constructorul uzual al clasei este cel care primete ca argument numele fontului, dimensiunea i stilul acestuia: s s Font(String name, int style, int size) Stilul unui font este specicat prin intermediul constantelor: Font.PLAIN, Font.BOLD, Font.ITALIC iar dimensiunea printr-un ntreg, ca exemplele n de mai jos: new Font("Dialog", Font.PLAIN, 12); new Font("Arial", Font.ITALIC, 14); new Font("Courier", Font.BOLD, 10); Folosirea unui obiect de tip Font se realizeaz uzual astfel: a // Pentru componente etichetate Label label = new Label("Un text"); label.setFont(new Font("Dialog", Font.PLAIN, 12)); // In metoda paint(Graphics g) g.setFont(new Font("Courier", Font.BOLD, 10)); g.drawString("Alt text", 10, 20); O platform de lucru are instalate, la un moment dat, o serie a ntreag de a fonturi care sunt disponibile pentru scrierea textelor. Lista acestor fonturi se poate obtine astfel: Font[] fonturi = GraphicsEnvironment. getLocalGraphicsEnvironment().getAllFonts(); Exemplul urmator aeaz lista tuturor fonturilor disponibile pe plats a forma curent de lucru. Textul ecrui nume de font va scris cu fontul su a a a corespunztor. a

278

CAPITOLUL 10. DESENAREA Listing 10.3: Lucrul cu fonturi

import java . awt .*; class Fonturi extends Canvas { private Font [] fonturi ; Dimension canvasSize = new Dimension (400 , 400) ; public Fonturi () { fonturi = Graphi c sE n v ir o nm e n t . g e t L o c a l G r a p h i c s E n v i r o n m e n t () . getAllFonts () ; canvasSize . height = (1 + fonturi . length ) * 20; } public void paint ( Graphics g ) { String nume ; for ( int i =0; i < fonturi . length ; i ++) { nume = fonturi [ i ]. getFontName () ; g . setFont ( new Font ( nume , Font . PLAIN , 14) ) ; g . drawString ( i + " . " + nume , 20 , ( i + 1) * 20) ; } } public Dimension getPreferredSize () { return canvasSize ; } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; ScrollPane sp = new ScrollPane () ; sp . setSize (400 , 400) ; sp . add ( new Fonturi () ) ; add ( sp , BorderLayout . CENTER ) ; pack () ; } } public class TestAllFonts { public static void main ( String args []) { new Fereastra ( " All fonts " ) . show () ; } }

10.3. FOLOSIREA FONTURILOR

279

10.3.2

Clasa FontMetrics

La aarea unui ir cu metoda drawString trebuie s specicm pozitia la s s a a care s apar textul respectiv pe ecran. In momentul care avem de aat a a n s mai multe iruri consecutiv, sau unele sub altele, trebuie s calculm pozitiile s a a lor de aare functie de lungimea i imea pixeli a celorlalte texte. s n s nalt n Pentru aceasta este folosit clasa FontMetrics. Un obiect din aceast clas a a a se construiete pornind de la un obiect de tip Font i pune la dispozitie s s informatii despre dimensiunile pixeli pe care le au caracterele fontului n respectiv ntr-un anumit context de desenare. Aadar, un obiect de tip FontMetrics s ncapsuleaz informatii despre a metrica unui font, cu alte cuvinte despre dimensiunile pixeli ale caracn terelor sale. Utilitatea principal a acestei clase const faptul c permite a a n a pozitionarea precis a textelor pe o suprafat de desenare, indiferent de fontul a a folosit de acestea. Metrica unui font const urmtoarele atribute pe care le au caracterele a n a sale: Linia de baz: este linia dup care sunt aliniate caracterele unui font; a a Linia de ascendent: linia superioara pe care nu o depaseste nici un a caracter din font Linia de descendent: linia inferioar sub care nu coboar nici un cara a a acter din font; Ascendentul: distanta ntre linia de baz i linia de ascendent; as a Descendentul: distanta ntre linia de baz i linia de descendent; as a Limea: limea unui anumit caracter din font; at at Distanta ntre linii (leading): distanta optim a ntre dou linii de text a scrise cu acelai font. s Inltimea: distanta dintre liniile de baz (leading+ascent+descent); a a Figura de mai jos prezint o imagine reprezentativ asupra metricii unui a a font:

280

CAPITOLUL 10. DESENAREA

Reamintim c la metoda drawString(String s, int x, int y) argua mentele x i y repreznit coltul din stnga-jos al textului. Ca s m mai s a a a precii, y reprezint pozitia liniei de baz a textului care va scris. s a a Un context grac pune la dispozitie o metod special getFontMetrics a a de creare a unui obiect de tip FontMetrics, pornind de la fontul curent al contextului grac: public void paint(Graphics g) { Font f = new Font("Arial", Font.BOLD, 11); FontMetrics fm = g.getFontMetrics(); } Cele mai folosite metode ale clasei FontMetrics sunt: getHeight - determin altimea unei linii pe care vor scrise caractere a n ale unui font; stringWidth - determin limea total pixeli a unui ir de caractere a at a n s specicat; charWidth - determin limea unui anumit caracter din font. a at In exemplul urmtor sunt aate pe ecran zilele sptmnii i lunile ana s a a a s ului:

10.3. FOLOSIREA FONTURILOR Listing 10.4: Folosirea clasei FontMetrics


import java . awt .*; class Texte extends Canvas { Dimension canvasSize = new Dimension (800 , 100) ; private String [] zile = { " Luni " , " Marti " , " Miercuri " , " Joi " , " Vineri " , " Sambata " , " Duminica " }; private String [] luni = { " Ianuarie " , " Februarie " , " Martie " , " Aprilie " , " Mai " , " Iunie " , " Iulie " , " August " , " Septembrie " , " Octombrie " , " Noiembrie " , " Decembrie " }; public void paint ( Graphics g ) { FontMetrics fm ; int x , y ; String etZile = " Zilele saptamanii : " , etLuni = " Lunile anului : " , text ; // Alegem un font si aflam metrica sa g . setFont ( new Font ( " Arial " , Font . BOLD , 20) ) ; fm = g . getFontMetrics () ; x = 0; y = fm . getHeight () ; g . drawString ( etZile , x , y ) ; x += fm . stringWidth ( etZile ) ; for ( int i =0; i < zile . length ; i ++) { text = zile [ i ]; if ( i < zile . length - 1) text += " , " ; g . drawString ( text , x , y ) ; x += fm . stringWidth ( text ) ; } // Schimbam fontul g . setFont ( new Font ( " Dialog " , Font . PLAIN , 14) ) ; fm = g . getFontMetrics () ; x = 0; y += fm . getHeight () ; g . drawString ( etLuni , x , y ) ; x += fm . stringWidth ( etLuni ) ; for ( int i =0; i < luni . length ; i ++) { text = luni [ i ]; if ( i < luni . length - 1)

281

282
text += " , " ; g . drawString ( text , x , y ) ; x += fm . stringWidth ( text ) ;

CAPITOLUL 10. DESENAREA

} } public Dimension getPreferredSize () { return canvasSize ; } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; add ( new Texte () , BorderLayout . CENTER ) ; pack () ; } } public class TestFontMetrics { public static void main ( String args []) { Fereastra f = new Fereastra ( " FontMetrics " ) ; f . show () ; } }

10.4

Folosirea culorilor

Orice culoare este format prin combinatia culorilor standard rou (red), a s verde (green) i albastru (blue), la care se adaug un anumit grad de transparent s a a (alpha). Fiecare din aceti patru parametri poate varia s ntr-un interval cuprins e ntre 0 i 255 (dac dorim s specicm valorile prin numere s a a a ntregi), e ntre 0.0 i 1.0 (dac dorim s specicm valorile prin numere s a a a reale). O culoare este reprezentat printr-o instant a clasei Color sau a suba a clasei sale SystemColor. Pentru a crea o culoare avem dou posibiliti: a at S folosim una din constantele denite cele dou clase; a n a S folosim unul din constructorii clasei Color. a S vedem mai ai care sunt constantele denite aceste clase: a nt n

10.4. FOLOSIREA CULORILOR Color black blue cyan darkGray gray green lightGray magenta orange pink red white yellow SystemColor activeCaption activeCaptionBorder activeCaptionText control controlHighlight controlShadow contolText desktop menu text textHighlight window ...

283

Observati c clasa Color sunt denite culori uzuale din paleta standard a n de culori, timp ce clasa SystemColor sunt denite culorile componenn n telor standard (ferestre, texte, meniuri, etc) ale platformei curente de lucru. Folosirea acestor constante se face ca exemplele de mai jos: n Color rosu = Color.red; Color galben = Color.yellow; Color fundal = SystemColor.desktop; Dac nici una din aceste culori predenite nu corespunde preferintelor noasa tre, atunci putem crea noi culori prin intermediul constructorilor clasei Color: Color(float red, flot green, float blue) Color(flot red, float green, float blue, float alpha) Color(int red, int green, int blue) Color(int red, int green, int blue, int alpha) Color(int rgb) unde red, green, blue, alpha sunt valorile pentru rou, verde, albastru i s s transparent iar parametrul rgb de la ultimul constructor reprezint un a a ntreg format din: bitii 16-23 rou, 8-15 verde, 0-7 albastru. s Valorile argumentelor variaz a ntre 0 255 pentru tipul int, respectiv 0.0 1.0 pentru tipul float. Valoarea 255 (sau 1.0) pentru transparent a specic faptul c respectiva culoare este complet opac, iar valoarea 0 (sau a a a 0.0) specic transparent total. Implicit, culorile sunt complet opace. a a a

284

CAPITOLUL 10. DESENAREA

// Exemple de folosire a constructorilor: Color alb = new Color(255, 255, 255); Color negru = new Color(0, 0, 0); Color rosu = new Color(255, 0, 0); Color rosuTransparent = new Color(255, 0, 0, 128); Metodele cele mai folosite ale clasei Color sunt: brighter darker getRed getGreen getBlue getAlpha getRGB Creeaz o noua versiune a culorii curente a mai deschis, respectiv mai a nchis a Determin parametrii din care a este alcatuit culoarea a Determin valoarea ce reprezint culoarea a a respectiv (bitii 16-23 rou, 8-15 verde, 0-7 albastru) a s

S considerm o aplicatie cu ajutorul creia putem vizualiza dinamic cua a a lorile obtinute prin diferite combinatii ale parametrilor ce formeaz o culoare. a Aplicatia va arta astfel: a

Listing 10.5: Folosirea clasei Color


import java . awt .*; import java . awt . event .*; class Culoare extends Canvas { public Color color = new Color (0 , 0 , 0 , 255) ; Dimension canvasSize = new Dimension (150 , 50) ; public void paint ( Graphics g ) { g . setColor ( Color . black ) ;

10.4. FOLOSIREA CULORILOR


g . setFont ( new Font ( " Arial " , Font . BOLD , 12) ) ; String text = " " ; text += " R = " + color . getRed () ; text += " G = " + color . getGreen () ; text += " B = " + color . getBlue () ; text += " A = " + color . getAlpha () ; g . drawString ( text , 0 , 30) ; g . setColor ( color ) ; g . fillRect (0 , 0 , canvasSize . width , canvasSize . height ) ; } public Dimension getPreferredSize () { return canvasSize ; } }

285

class Fereastra extends Frame implements Ad ju st me nt Lis te ne r { private Scrollbar rValue , gValue , bValue , aValue ; private Culoare culoare ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; Panel rgbValues = new Panel () ; rgbValues . setLayout ( new GridLayout (4 , 1) ) ; rValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ; rValue . setBackground ( Color . red ) ; gValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ; gValue . setBackground ( Color . green ) ; bValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ; bValue . setBackground ( Color . blue ) ; aValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ;

286

CAPITOLUL 10. DESENAREA


aValue . setValue (255) ; aValue . setBackground ( Color . lightGray ) ; rgbValues . add ( rValue ) ; rgbValues . add ( gValue ) ; rgbValues . add ( bValue ) ; rgbValues . add ( aValue ) ; rgbValues . setSize (200 , 100) ; add ( rgbValues , BorderLayout . CENTER ) ; culoare = new Culoare () ; add ( culoare , BorderLayout . NORTH ) ; pack () ; rValue . add Adjus t m e n t L i s t e n e r ( this ) ; gValue . add Adjus t m e n t L i s t e n e r ( this ) ; bValue . add Adjus t m e n t L i s t e n e r ( this ) ; aValue . add Adjus t m e n t L i s t e n e r ( this ) ;

} public void ad ju stm en tV al u e C h an ge d ( AdjustmentEvent e ) { int r = rValue . getValue () ; int g = gValue . getValue () ; int b = bValue . getValue () ; int a = aValue . getValue () ; Color c = new Color (r , g , b , a ) ; culoare . color = c ; culoare . repaint () ; } } public class TestColor { public static void main ( String args []) { Fereastra f = new Fereastra ( " Color " ) ; f . show () ; } }

10.5

Folosirea imaginilor

Aceasta este o imagine:

10.5. FOLOSIREA IMAGINILOR

287

In AWT este posibil folosirea imaginilor create extern format gif sau a n jpeg. Orice imagine va reprezentat ca o instant a clasei Image. Aceasta a a nu este o clas de componente (nu extinde Component) ci implementeaz a a obiecte care pot desenate pe suprafata unor componente cu metode specice unui context grac pentru componenta respectiva (similar modului cum se deseneaz o linie sau un cerc). a

10.5.1

Aarea imaginilor s

Aarea unei imagini presupune realizarea urmtoarilor doi pai: s a s 1. Crearea unui obiect de tip Image; 2. Aarea propriu-zis s a ntr-un context grac; Crearea unui obiect de tip Image se face folosind o imagine dintr-un ier s e aat pe maina pe care se lucreaz, e aat la o anumit adres Web s a a a (URL). Metodele pentru arcarea unei imagini dintr-un ier se gsesc nc s a clasele Applet i Toolkit, avnd a aceeai denumire getImage i n s a ns s s urmtoarele formate: a Applet Toolkit getImage(URL url) getImage(URL url) getImage(URL url, String fisier) getImage(String fisier) Pentru a obtine un obiect de tip Toolkit se va folosi metoda getDefaultToolkit, ca exemplul de mai jos: n Toolkit toolkit = Toolkit.getDefaultToolkit(); Image image1 = toolkit.getImage("poza.gif"); Image image2 = toolkit.getImage( new URL("http://www.infoiasi.ro/~acf/poza.gif"));

288

CAPITOLUL 10. DESENAREA

Metoda getImage nu veric dac ierul sau adresa specicata reprezint a a s a o imagine valid i nici nu as ncarc efectiv imaginea memorie, aceste operatiuni a n ind fcute abia momentul care se va realiza aarea imaginii pentru a n n s prima dat. Metoda nu face dect s creeze un obiect de tip Image care face a a a referint la o anumit imagine extern. a a a

Aarea unei imagini s ntr-un context grac se realizeaz prin intermediul a metodei drawImage din clasa Graphics i, general, va facut metoda s n a n paint a unei componente. Cele mai uzuale formate ale metodei sunt: boolean drawImage(Image img, int boolean drawImage(Image img, int ImageObserver observer) boolean drawImage(Image img, int ImageObserver observer) boolean drawImage(Image img, int Color bgcolor, ImageObserver unde: img este obiectul ce reprezint imaginea; a x, y sunt coordonatele stnga-sus la care va aat imaginea, relative a s a la spatiul de coordonate al contextului grac; observer este un obiect care observ arcarea imaginii i va ina nc s format pe msura derulrii acesteia; a a width, heigth reprezint imea i limea la care trebuie scalat a nalt s at a imaginea (dac lipsesc, imaginea va aat la dimensiunile ei reale); a s a bgColor reprezint culoarea cu care vor colorati pixelii transparenti a ai imaginii (poate s lipseasc). a a In exemplul urmtor aam aceeai imagine de trei ori, folosind forme a s s diferite ale metodei drawImage: Image img = Toolkit.getDefaultToolkit().getImage("taz.gif"); g.drawImage(img, 0, 0, this); g.drawImage(img, 0, 200, 100, 100, this); g.drawImage(img, 200, 0, 200, 400, Color.yellow, this); x, int y, ImageObserver observer) x, int y, Color bgcolor, x, int y, int width, int height, x, int y, int width, int height, observer)

10.5. FOLOSIREA IMAGINILOR

289

Metoda drawImage returneaz true dac imaginea a fost aat a a s a n ntregime i false caz contrar, cu alte cuvinte metoda nu atept ca o imagine s s n s a a e complet aat ci se termin imediat ce procesul de aare a s a a s nceput. In sectiunea urmtoare vom detalia acest aspect. a

10.5.2

Monitorizarea arcrii imaginilor nc a

In cazul care se aeaz o imagine care se gsete pe Internet sau imaginea n s a a s aata este de dimensiuni mari se va observa c aceasta nu apare complet de s a la nceput ci este desenat treptat, fr interventia programatorului. Acest a aa lucru se ampl deoarece metoda drawImage nu face dect s declaneze nt a a a s procesul de arcare i desenare a imaginii, dup care red imediat connc s a a trolul apelantului, lucru deosebit de util ntruct procesul de arcare a a nc unei imagini poate dura mult i nu este de dorit ca acest interval de timp s n (pn la arcarea complet a imaginii) aplicatia s e blocat. Ca urmare, a a nc a a a la apelul metodei drawImage va desenat numai portiunea de imagine care a este disponibil la momentul initial i care poate incomplet. De aceea a s a trebuie s existe un mecanism prin care componenta s e redesenat aua a a tomat momentul care au mai sosit informatii legate de imagine, pn la n n a a aarea sa complet. Acest mecanism este realizat prin intermediul interfetei s a ImageObserver, implementat de clasa Component i deci de toate compoa s nentele. Aceast interfat descrie obiecte care au a a nceput s utilizeze o imaga ine incomplet i care trebuie anuntate de noile date obtinute legatur cu as n a imaginea respectiv. a Interfata ImageObserver are o singur metod numit imageUpdate, a a a ce va apelat periodic de rul de executie (creat automat) care se ocup a a cu arcarea imaginii. Formatul acestei metode este: nc boolean imageUpdate (Image img, int flags, int x, int y, int w, int h ) Implementarea implicit const dintr-un apel la metoda repaint pentru a a dreptunghiul specicat la apel i care reprezint zona din imagine pentru care s a se cunosc noi informatii. Intregul f lags furnizeaz informatii despre starea a transferului. Aceste informatii pot aate prin intermediul constantelor denite de interfat: a

290 ABORT

CAPITOLUL 10. DESENAREA Incrcarea imaginii a fost a ntrerupt, a nainte de completarea sa ALLBITS Imaginea a fost ncarcat complet a ERROR A aprut o eroare timpul a n arcrii imaginii nc a FRAMEBITS Toti bitii cadrului curent sunt disponibili HEIGHT Inltimea imaginii este disponibil a a PROPERTIES Proprietile imaginii sunt disponibile at SOMEBITS Au fost receptionati noi pixeli ai imaginii WIDTH Limea imaginii este disponibil at a

Prezenta parametrul f lags a unui bit de valoare 1 pe pozitia reprezen n tata de o constant a nseamn c respectiva conditie este a a ndeplinit. a // Imaginea este completa (flags & ALLBITS) != 0 // Eroare sau transferul imaginii a fost intrerupt (flags & ERROR | ABORT ) != 0 Metoda imageUpdate poate redent de o component pentru a pera a sonaliza procesul de aare al imaginii. Aceasta va apelat apelat asincron s a a de ecare dat cnd sunt disponibili noi pixeli. a a public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) { // Desenam imaginea numai daca toti bitii sunt disponibili if (( flags & ALLBITS) != 0) repaint(); // Daca sunt toti bitii nu mai sunt necesare noi update-uri return ( (flags & (ALLBITS | ABORT)) == 0); } De asemenea, se observ c metodele clasei Image pentru determinarea a a dimensiunilor unei imagini au ca argument un obiect de tip ImageObserver. int getHeight(ImageObserver observer) int getWidth(ImageObserver observer) Dac desenarea se face folosind clasa Canvas, atunci argumentul observer a al metodelor referitoare la imagini va this.

10.5. FOLOSIREA IMAGINILOR

291

10.5.3

Mecanismul de double-buering

Tehnica de double-buering implic realizarea unui desen memorie i apoi a n s transferul su pe ecran, pentru a elimina efectul neplcut de clipire (icka a ering) rezultat atunci cnd sunt efectuate redesenri repetate la intervale a a mici de timp. O situatie frecvent care se apeleaz la double-buering este a n a crearea de animatii. Secventa general de implementare a mecanismului de double-buering a este urmtoarea: a // Supradefinim update pentru a elimina stergerea desenului public void update(Graphics g) { paint(g); } public void paint(Graphics g) { // Desenam in memorie pe un obiect de tip Image // w si h sunt dimensiunile desenului Image img = createImage(w, h); Graphics gmem = img.getGraphics(); /* Realizam desenul folosind gmem gmem.setColor(...); gmem.fillOval(...); ... */ // Transferam desenul din memorie pe ecran // desenand de fapt imaginea creata g.drawImage(img, 0, 0, this); gmem.dispose(); } }

10.5.4

Salvarea desenelor format JPEG n

Pachetul com.sun.image.codec.jpeg din distributia standard Java ofer a suport pentru salvarea unei imagini aate memorie n ntr-un ier fors n

292

CAPITOLUL 10. DESENAREA

mat JPEG. O clas responsabil cu realizarea acestei operatiuni ar putea a a denit astfel: a import import import import com.sun.image.codec.jpeg.*; java.awt.image.BufferedImage; java.awt.*; java.io.*;

class JPEGWriter { static float quality = 0.9f; //intre 0 si 1 public static void write(BufferedImage img, String filename) { try { FileOutputStream out = new FileOutputStream(filename); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); JPEGEncodeParam jep = encoder.getDefaultJPEGEncodeParam(img); jep.setQuality(quality, false); // Folosim setarile de codare jpeg implicite encoder.setJPEGEncodeParam(jep); encoder.encode(img); out.close(); } catch( Exception e ) { e.printStackTrace(); } } }

10.5.5

Crearea imaginilor memorie n

In cazul care dorim s folosim o anumit imagine creat direct din pron a a a gram i nu s ncarcat dintr-un ier vom folosi clasa MemoryImageSource, a s aata pachetul java.awt.image. Pentru aceasta va trebui s denim un n a

10.6. TIPARIREA

293

vector de numere ntregi care vom scrie valorile n ntregi (RGB) ale culorilor pixelilor ce denesc imaginea noastr. Dimensiunea vectorului va a altimea n nmultit cu limea pixeli a imaginii. Constructorul clasei a at n MemoryImageSource este: MemoryImageSource(int w, int h, int[] pixeli, int off, int scan) unde: w, h reprezint dimensiunile imaginii (limea i altimea); a at s n pixeli[] este vectorul cu culorile imaginii; of f, scan reprezint modalitatea de construire a matricii imaginii pornind a de la vectorul cu pixeli, normal aceste valori sunt o = 0, scan = w In exemplul urmator vom crea o imagine cu pixeli de culori aleatorii i o s vom aa pe ecran: s int w = 100; int h = 100; int[] pix = new int[w * h]; int index = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int red = (int) (Math.random() * 255); int green = (int) (Math.random() * 255); int blue = (int) (Math.random() * 255); pix[index++] = new Color(red, green, blue).getRGB(); } img = createImage(new MemoryImageSource(w, h, pix, 0, w)); g.drawImage(img, 0, 0, this); // g este un context grafic

10.6

Tiprirea a

Tiprirea Java este tratat aceeai manier ca i desenarea, singurul a n a n s a s lucru diferit ind contextul grac care se execut operatiile. Pachetul n a care ofer suport pentru tiprire este java.awt.print, iar clasa principal a a a care controleaz procesul de tiprire este PrinterJob. O aplicatie va apela a a metode ale acestei clase pentru:

294

CAPITOLUL 10. DESENAREA

Crearea unei sesiuni de tiprire (job); a Invocarea dialogului cu utilizatorul pentru specicarea unor parametri legati de tiprire; a Tiprirea efectiv. a a Orice component care poate aat pe ecran poate i tiprit. In gena s a s a a eral, orice informatii care trebuie att aate ct i tiprite, vor a s a s a ncapsulate ntr-un obiect grac - component, care are o reprezentare vizual descris a a a de metoda paint i care va specica i modalitatea de reprezentare a sa la s s imprimant. a Un obiect care va tiprit trebuie s implementeze interfata Printable, a a care contine o singur metod print, responsabil cu descrierea modalitii a a a at de tiprire a obiectului. In cazul cnd imaginea de pe ecran coincide cu a a imaginea de la imprimant, metodele paint i print pot specica aceeai a s s secvent de cod. In general, metoda print are urmtorul format: a a public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { // Descrirea imaginii obiectului ce va fi afisata la imprimanta // Poate fi un apel la metoda paint: paint(g) if (ceva nu este in regula}) { return Printable.NO_SUCH_PAGE; } return Printable.PAGE_EXISTS; } Paii care trebuie efectuati pentru tiprirea unui obiect sunt: s a 1. Crearea unei sesiuni de tiprire: PrinterJob.getPrinterJob a 2. Specicarea obiectului care va tiprit: setPrintable; acesta trebuie a s implementeze interfata Printable; a 3. Optional, initierea unui dialog cu utilizatorul pentru precizarea unor parametri legati de tiprire: printDialog; a

10.6. TIPARIREA 4. Tiprirea efectiv: print. a a

295

In exemplul urmtor vom deni un obiect care are aceeai reprezentare a s pe ecran ct i la imprimant (un cerc circumscris unui ptrat, a s a a nsotite de un text) i vom tipri obiectul respectiv. s a Listing 10.6: Tiprirea unei componente a
import import import import java . io .*; java . awt .*; java . awt . event .*; java . awt . print .*;

class Plansa extends Canvas implements Printable { Dimension d = new Dimension (400 , 400) ; public Dimension getPreferredSize () { return d ; } public void paint ( Graphics g ) { g . drawRect (200 , 200 , 100 , 100) ; g . drawOval (200 , 200 , 100 , 100) ; g . drawString ( " Hello " , 200 , 200) ; } public int print ( Graphics g , PageFormat pf , int pi ) throws PrinterException { if ( pi >= 1) return Printable . NO_SUCH_PAGE ; paint ( g ) ; g . drawString ( " Numai la imprimanta " , 200 , 300) ; return Printable . PAGE_EXISTS ; } } class Fereastra extends Frame implements ActionListener { private Plansa plansa = new Plansa () ; private Button print = new Button ( " Print " ) ; public Fereastra ( String titlu ) { super ( titlu ) ; addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) {

296
System . exit (0) ; } }) ;

CAPITOLUL 10. DESENAREA

add ( plansa , BorderLayout . CENTER ) ; Panel south = new Panel () ; south . setLayout ( new FlowLayout ( FlowLayout . CENTER ) ) ; south . add ( print ) ; add ( south , BorderLayout . SOUTH ) ; print . addActionListe ner ( this ) ; pack () ; } public void actionPerformed ( ActionEvent e ) { // 1. Crearea unei sesiuni de tiparire PrinterJob printJob = PrinterJob . getPrinterJob () ; // 2. Stabilirea obiectului ce va fi tiparit printJob . setPrintable ( plansa ) ; // 3. Initierea dialogului cu utilizatorul if ( printJob . printDialog () ) { try { // 4. Tiparirea efectiva printJob . print () ; } catch ( PrinterException ex ) { System . out . println ( " Exceptie la tiparire ! " ) ; ex . printStackTrace () ; } } } } public class TestPrint { public static void main ( String args []) throws Exception { Fereastra f = new Fereastra ( " Test Print " ) ; f . show () ; } }

10.6. TIPARIREA

297

Tiparirea textelor O alt variant pentru tiprirea de texte este deschiderea unui ux ctre a a a a dispozitivul special reprezentat de imprimant i scrierea informatiilor, linie as cu linie, pe acest ux. In sistemul de operare Windows, imprimanta poate referit prin lpt1, iar Unix prin /dev/lp. Observati c aceast a n a a abordare nu este portabil, deoarece necesit tratare special functie de a a a n sistemul de operare folosit. Listing 10.7: Tiprirea textelor a
import java . io .*; import java . awt .*; class TestPrintText { public static void main ( String args []) throws Exception { // pentru Windows PrintWriter imp = new PrintWriter ( new FileWriter ( " lpt1 " ) ) ; // pentru UNIX // PrintWriter imp = new PrintWriter ( new FileWriter ("/ dev / lp ") ) ; imp . println ( " Test imprimanta " ) ; imp . println ( " ABCDE " ) ; imp . close () ; } }

298

CAPITOLUL 10. DESENAREA

Capitolul 11 Swing
11.1
11.1.1

Introducere
JFC

Tehnologia Swing face parte dintr-un proiect mai amplu numit JFC (Java Foundation Classes) care pune la dispozitie o serie ntreag de faciliti pena at tru scrierea de aplicatii cu o interfat grac mult a a mbogit functional i at a s estetic fat de vechiul model AWT. In JFC sunt incluse urmtoarele: a a Componente Swing Sunt componente ce nlocuiesc i acelai timp extind vechiul set oferit s n s de modelul AWT. Look-and-Feel Permite schimbarea airii i a modului de interactiune cu aplicatia nft sa s functie de preferintele ecruia. Acelai program poate utiliza din a s verse moduri Look-and-Feel, cum ar cele standard Windows, Mac, Java, Motif sau altele oferite de diveri dezvoltatori, acestea putnd s a interschimbate de ctre utilizator chiar la momentul executiei . a Accessibility API Permite dezvoltarea de aplicatii care s comunice cu dispozitive uti a lizate de ctre persoane cu diverse tipuri de handicap, cum ar cititoare a de ecran, dispozitive de recunoatere a vocii, ecrane Braille, etc. s Java 2D API Folosind Java 2D pot create aplicatii care utilizeaz grac la un a a 299

300

CAPITOLUL 11. SWING nivel avansat. Clasele puse la dispozitie permit crearea de desene com plexe, efectuarea de operatii geometrice (rotiri, scalri, translatii, etc.), a prelucrarea de imagini, tiprire, etc. a

Drag-and-Drop Ofer posibilitatea de a efectua operatii drag-and-drop a ntre aplicatii Java i aplicatii native. s Internationalizare Internationalizarea i localizarea aplicatiilor sunt dou faciliti extrem s a at de importante care permit dezvoltarea de aplicatii care s poat con a a gurate pentru exploatarea lor diverse zone ale globului, utiliznd n a limba i particularitile legate de formatarea datei, numerelor sau a s at monedei din zona respectiv. a In aceste capitol vom face o prezentare scurt a componentelor Swing, a deoarece prezentarea detaliata a tuturor facilitilor oferite de JFC ar oferi at sucient material pentru un volum de sine stttor. aa

11.1.2

Swing API

Unul din principalele deziderate ale tehnologiei Swing a fost s pun la a a dispozitie un set de componente GUI extensibile care s permit dezvoltarea a a rapid de aplicatii Java cu interfat grac competitiv din punct de vedere a a a a comercial. Pentru a realiza acest lucru, API-ul oferit de Swing este deosebit de complex avnd 17 pachete care se gsesc sute de clase i interfete. Lista a n a s complet a pacehetelor din distributia standard 1.4 este dat tabelul de a a n mai jos: javax.accessibility javax.swing.text.html javax.swing.plaf.basic javax.swing.border javax.swing.text.rtf javax.swing.plaf.multi javax.swing.event javax.swing.undo javax.swing.text javax.swing.plaf javax.swing javax.swing.text.parser javax.swing.plaf.metal javax.swing.colorchooser javax.swing.tree javax.swing.table javax.swing.filechooser

11.1. INTRODUCERE

301

Evident, nu toate aceste pachete sunt necesare la dezvolatarea unei aplicatii, cel mai important i care contine componentele de baz ind javax.swing. s a

Componentele folosite pentru crearea interfetelor grace Swing pot gru pate astfel: Componente atomice JLabel, JButton, JCheckBox, JRadioButton, JToggleButton, JScrollBar, JSlider, JProgressBar, JSeparator Componente complexe JTable, JTree, JComboBox, JSpinner, JList, JFileChooser, JColorChooser, JOptionPane Componente pentru editare de text JTextField, JFormattedTextField, JPasswordField, JTextArea, JEditorPane, JTextPane Meniuri JMenuBar, JMenu, JPopupMenu, JMenuItem, JCheckboxMenuItem, JRadioButtonMenuItem Containere intermediare JPanel, JScrollPane, JSplitPane, JTabbedPane, JDesktopPane, JToolBar Containere de nivel nalt JFrame, JDialog, JWindow, JInternalFrame, JApplet

11.1.3

Asemnri i deosebiri cu AWT a a s

Nu se poate spune c Swing a nlocuiete modelul AWT ci extinde pe acesta s l din urm adugndu-i noi componente care e a a a nlocuiesc unele vechi e sunt cu totul noi. O conventie general respectat este prexarea numelui unei n a clase AWT cu litera J pentru a denumi clasa corespondent din Swing. a Astfel, locul clasei java.awt.Button putem folosi javax.swing.JButton, n loc de java.awt.Label putem folosi javax.swing.JLabel, etc. Este recon mandat ca o aplicatie cu interfat grac s foloseasc e componente AWT, a a a a e Swing, amestecarea lor ind mai putin uzual. a

302

CAPITOLUL 11. SWING

Aplicatiile GUI vor avea continuare nevoie de pachetul java.awt deoarece n aici sunt denite unele clase utilitare cum ar Color, Font, Dimension, etc. care nu au fost rescrise Swing. n De asemenea, pachetul java.awt.event rmne continuare esential a a n pentru tratarea evenimentelor generate att de componente AWT ct i de a a s cele din Swing. Pe lng acesta mai poate necesar i javax.swing.event a a s care descrie tipuri de evenimente specice unor componente Swing, mecanismul de tratare a lor ind a acelai ca AWT. ns s n Pozitionarea componentelor este preluat din AWT, ind adugate a a a ns noi clase care descriu gestionari de pozitionare completarea celor exis n tente, cum ar BoxLayout i SpringLayout. Difer a modul de lucru cu s a ns containere, dup cum vom vedea sectiunea dedicat acestora. a n a Majoritatea componentelor Swing care permit aarea unui text ca parte s a reprezentrii lor GUI pot specica acel text e mod normal folosind un a n anumit font i o anumit culoare ce pot setate cu metodele setFont i s a s setColor, e prin intermediul limbajului HTML. Folosirea HTML aduce o exibilitatea deosebit realizarea interfetei grace, a n ntruct putem aplica a formatri multiple unui text, descompunerea acestuia pe mai multe linii, etc., a singurul dezavantaj ind ncetinirea etapei de aare a componentelor. s JButton simplu = new JButton("Text simplu"); JButton html = new JButton( "<html><u>Text</u> <i>formatat</i></html>"); S descriem o aplictie simpl folosind AWT i apoi Swing, pentru a ne a a s crea o prim impresie asupra diferentelor i asemnrilor dintre cele dou a s a a a modele. Listing 11.1: O aplicatie simpl AWT a
import java . awt .*; import java . awt . event .*; public class ExempluAWT extends Frame implements ActionListener { public ExempluAWT ( String titlu ) { super ( titlu ) ; setLayout ( new FlowLayout () ) ; add ( new Label ( " Hello AWT " ) ) ; Button b = new Button ( " Close " ) ; b . addActionListener ( this ) ;

11.1. INTRODUCERE
add ( b ) ; pack () ; show () ; } public void actionPerformed ( ActionEvent e ) { System . exit (0) ; } public static void main ( String args []) { new ExempluAWT ( " Hello " ) ; } }

303

Listing 11.2: Aplicatia rescris folosind Swing a


import javax . swing .*; import java . awt .*; import java . awt . event .*; public class ExempluSwing extends JFrame implements ActionListener { public ExempluSwing ( String titlu ) { super ( titlu ) ; // Metoda setLayout nu se aplica direct ferestrei getContentPane () . setLayout ( new FlowLayout () ) ; // Componentele au denumiri ce incep cu litera J // Textul poate fi si in format HTML getContentPane () . add ( new JLabel ( " < html > <u > Hello </ u > <i > Swing </ i > </ html > " ) ) ; JButton b = new JButton ( " Close " ) ; b . addActionListener ( this ) ; // Metoda add nu se aplica direct ferestrei getContentPane () . add ( b ) ; pack () ; show () ; } public void actionPerformed ( ActionEvent e ) { // Tratarea evenimentelor se face ca in AWT System . exit (0) ; } public static void main ( String args []) { new ExempluSwing ( " Hello " ) ; }

304
}

CAPITOLUL 11. SWING

11.2

Folosirea ferestrelor

Pentru a aate pe ecran componentele grace ale unei aplicatii trebuie s plasate pe o suprafat de aare (container). Fiecare component poate a s a continut doar a ntr-un singur container, adugarea ei pe o supraft nou a a a de aare determinnd eliminarea ei de pe vechiul container pe care fusese s a plasat. Intruct containerele pot a a ncapsulate alte containere, o comn ponent va face parte la un moment dat dintr-o ierarhie. Rdcina acestei a a a ierarhii trebuie s e un aa numit container de nivel a s nalt, care este reprezentat de una din clasele JFrame, JDialog sau JApplet. Intruct de appleturi a ne vom ocupa separat, vom analiza continuare primele dou clase. n a In general orice aplicatie Java independent bazat pe Swing contine a a cel putin un container de nivel nalt reprezentat de fereastra principal a a programului, instant a clasei JFrame. a Simplicat, un obiect care reprezint o fereastr Swing contine o zon a a a care este rezervat barei de meniuri i care este situat de obieci partea sa a s a n superioar i corpul ferestrei pe care vor plasate componentele. Imaginea as de mai jos pune evident aceast separare, valabil de altfel pentru orice n a a a container de nivel nalt:

Corpul ferestrei este o instant a clasei Container ce poate obtinut cu a a metoda getContentPane. Plasarea i aranjarea componentelor pe suprafata s

11.2. FOLOSIREA FERESTRELOR

305

ferestrei se va face deci folosind obiectul de tip Container i nu direct fereass tra. Aadar, dei este derivat din Frame, clasa JFrame este folosit s s a a ntr-un mod diferit fat de printele su: a a a Frame f = new Frame(); f.setLayout(new FlowLayout()); f.add(new Button("OK")); JFrame jf = new JFrame(); jf.getContentPane().setLayout(new FlowLayout()); jf.getContentPane().add(new JButton("OK")); Spre deosebire de Frame, un obiect JFrame are un comportament implicit la nchiderea ferestrei care const ascunderea ferestrei atunci cnd utilizaa n a torul apas butonul de a nchidere. Acest comportament poate modicat prin apelarea metodei setDefaultCloseOperation care primete ca argus ment diverse constante ce se gsesc e clasa WindowConstants, e chiar a n JFrame. n jf.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Adugarea unei bare de meniuri se realizeaz cu metoda setJMenuBar, a a care primete o instant de tip JMenuBar. Crearea meniurilor este similar s a a cu modelul AWT.

11.2.1

Ferestre interne

Din punctul de vedere al folosirii ferestrelor, aplicatiile pot artite mp n dou categorii: a SDI (Single Document Interface) MDI (Multiple Document Interface) Programele din prima categorie gestioneaz la un moment dat o singur a a fereastr care se gsesc componentele cu care interactioneaz utilizatorul. a n a a In a doua categorie, fereastra principal a aplicatiei a nglobeaz la rndul ei a a alte ferestre, uzual cu functionaliti similare, ce permit lucrul concurent pe at mai multe planuri.

306

CAPITOLUL 11. SWING

In Swing, clasa JInternalFrame pune la dispozitie o modalitate de a crea ferestre cadrul altor ferestre. Ferestrele interne au aproximativ aceeai n s aiare i functionalitate cu ferestrele de tip JFrame, singura diferent ind nft s s a modul de gestionare a acestora. Uzual, obiectele de tip JInternalFrame vor plasate pe un container de tip DesktopPane, care va apoi plasat pe o fereastr de tip JFrame. a Folosirea clasei DesktopPane este necesar deoarece aceasta tie cum s a s a gestioneze ferestrele interne, avnd vedere c acestea se pot suprapune i a n a s la un moment dat doar una singur este activ. a a Exemplul urmtor pune evident modelul general de creare i aare a n a s s a ferestrelor interne: Listing 11.3: Folosirea ferestrelor interne
import javax . swing .*; import java . awt .*; class FereastraPrinci pal a extends JFrame { public Fereastra Pr i nc i p al a ( String titlu ) { super ( titlu ) ; setSize (300 , 200) ; s e tD e f au l t C lo s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ; FereastraInterna fin1 = new FereastraInterna () ; fin1 . setVisible ( true ) ; FereastraInterna fin2 = new FereastraInterna () ; fin2 . setVisible ( true ) ; JDesktopPane desktop = new JDesktopPane () ; desktop . add ( fin1 ) ; desktop . add ( fin2 ) ; setContentPane ( desktop ) ; fin2 . moveToFront () ; } } class FereastraInterna extends JInternalFrame { static int n = 0; // nr . de ferestre interne static final int x = 30 , y = 30; public FereastraInterna () { super ( " Document # " + (++ n ) ,

11.3. CLASA JCOMPONENT


true , // resizable true , // closable true , // maximizable true ) ; // iconifiable setLocation ( x *n , y * n ) ; setSize ( new Dimension (200 , 100) ) ;

307

} } public class TestInternalFrame { public static void main ( String args []) { new FereastraPrincipala ( " Test ferestre interne " ) . show () ; } }

Ferestrele create de acest program vor arta ca gura de mai jos: a n

11.3

Clasa JComponent

JComponent este superclasa tuturor componentelor Swing, mai putin a celor care descriu containere de nivel nalt JFrame, JDialog, JApplet. Deoarece JComponent extinde clasa Container, deci i Component, ea motenete functionalitatea s s s general a containerelor i componentelor AWT, furniznd bine eles i o sea s a nt s rie ntreag de noi faciliti. a at Dintre noutile oferite de JComponent amintim: at ToolTips Folosind metoda setToolTip poate ataat unei componente un text s cu explicatii legate de componenta respectiv. Cnd utilizatorul trece a a

308

CAPITOLUL 11. SWING cu mouse-ul deasupra componentei va aat, pentru o perioad de s a timp, textul ajuttor specicat. a

Chenare Orice component Swing poate avea unul sau mai multe chenare. Specia carea unui chenar se realizeaz cu metoda setBorder. a Suport pentru plasare i dimensionare s Folosind metodele setPreferredSize, setMinimumSize, setMaximumSize, setAlignmentX, setAlignmentY pot controlati parametrii folositi de gestionarii de pozitionare pentru plasarea i dimensionarea automat s a a componentelor cadrul unui container. n Controlul opacitii at Folosind metoda setOpaque vom specica dac o component trebuie a a sau nu s deseneze toti pixelii din interiorul su. Implicit, valoarea a a proprietii de opacitate este false, ceea ce at nseamn c este posibil a a s nu e desenati unii sau chiar toti pixelii, permitnd pixelilor de sub a a component s rmn vizibili (componenta nu este opac). Valoarea a a a a a a proprietii pentru clasele derivate din JComponent depinde general at n de Look-and-Feel-ul folosit. Asocierea de actiuni tastelor Pentru componentele Swing exist posibilitatea de specica anumite a actiuni care s se execute atunci cnd utilizatorul apas o anumit a a a a combinatie de taste i componenta respectiv este activ (are focus s a a ul). Aceast facilitate simplic varianta initial de lucru, i anume a a a s tratarea evenimentelor de tip KeyEvent printr-un obiect KeyListener. Double-Buering Tehnica de double-buering, care implic desenarea componentei a n memorie i apoi transferul s ntregului desen pe ecran, este implementat a automat de componentele Swing, spre deosebire de cele AWT unde trebuia realizat manual dac era cazul. a a Exemplul urmtor ilustreaz modul de folosire a ctorva dintre facilitile a a a at amintite mai sus: Listing 11.4: Faciliti oferite de clasa JComponent at

11.3. CLASA JCOMPONENT


import import import import javax . swing .*; javax . swing . border .*; java . awt .*; java . awt . event .*;

309

class Fereastra extends JFrame { public Fereastra ( String titlu ) { super ( titlu ) ; getContentPane () . setLayout ( new FlowLayout () ) ; s e tD e f au l t C lo s e Op e r at i o n ( JFrame . EXIT_ON_CLOSE ) ; // Folosirea chenarelor Border lowered , raised ; TitledBorder title ; lowered = BorderFactory . c r e a t e L o w e r e d B e v e l B o r d e r () ; raised = BorderFactory . c r e a t e R a i s e d B e v e l B o r d e r () ; title = BorderFactory . c re at eT it led Bo rd er ( " Borders " ) ; final JPanel panel = new JPanel () ; panel . setPreferredSize ( new Dimension (400 ,200) ) ; panel . setBackground ( Color . blue ) ; panel . setBorder ( title ) ; getContentPane () . add ( panel ) ; JLabel label1 = new JLabel ( " Lowered " ) ; label1 . setBorder ( lowered ) ; panel . add ( label1 ) ; JLabel label2 = new JLabel ( " Raised " ) ; label2 . setBorder ( raised ) ; panel . add ( label2 ) ; // Controlul opacitatii JButton btn1 = new JButton ( " Opaque " ) ; btn1 . setOpaque ( true ) ; // implicit panel . add ( btn1 ) ; JButton btn2 = new JButton ( " Transparent " ) ; btn2 . setOpaque ( false ) ; panel . add ( btn2 ) ; // ToolTips label1 . setToolTipText ( " Eticheta coborata " ) ; label2 . setToolTipText ( " Eticheta ridicata " ) ;

310

CAPITOLUL 11. SWING


btn1 . setToolTipText ( " Buton opac " ) ; // Textul poate fi HTML btn2 . setToolTipText ( " < html > <b > Apasati < font color = red > F2 </ font > " + " cand butonul are <u > focusul </ u > " ) ; // Asocierea unor actiuni ( KeyBindings ) /* Apasarea tastei F2 cand focusul este pe butonul al doilea va determina schimbarea culorii panelului */ btn2 . getInputMap () . put ( KeyStroke . getKeyStroke ( " F2 " ) , " schimbaCuloare " ) ; btn2 . getActionMap () . put ( " schimbaCuloare " , new AbstractAction () { private Color color = Color . red ; public void actionPerformed ( ActionEvent e ) { panel . setBackground ( color ) ; color = ( color == Color . red ? Color . blue : Color . red ) ; } }) ; pack () ;

} } public class TestJComponent { public static void main ( String args []) { new Fereastra ( " Facilitati JComponent " ) . show () ; } }

11.4 11.5

Arhitectura modelului Swing Folosirea modelelor

Modelul Swing este bazat pe o arhitectur asemntoare cu MVC (modela a a view-controller). Arhitectura MVC specic descompunerea unei aplicatii a vizuale trei prti separate: n a Modelul - care va reprezenta datele aplicatiei.

11.5. FOLOSIREA MODELELOR Prezentarea - modul de reprezentare vizual a datelor. a

311

Controlul - transformarea actiunilor utilizatorului asupra componen telor vizuale evenimente care s actualizeze automat modelul acesn a tora (datele). Din motive practice, Swing prtile de prezentare i control au fost cun a s plate deoarece exista o legtur prea strns a a a a ntre ele pentru a concepute ca entiti separate. Aadar, arhitectura Swing este de fapt o arhitectur at s a cu model separabil, care datele componentelor (modelul) sunt separate de n reprezentarea lor vizual. Aceast abordare este logic i din perspectiva a a a s faptului c, general, modul de concepere a unei aplicatii trebuie s e oria n a entat asupra reprezentrii i manipulrii informatiilor i nu asupra interfetei a s a s grace cu utilizatorul. Pentru a realiza separarea modelului de prezentare, ecrui obiect corea spunztor unei clase ce descrie o component Swing este asociat un obiect a a i care gestioneaz datele sale i care implementeaz o interfat care reprezint a s a a a modelul componentei respective. Dup cum se observ din tabelul de mai a a jos, componente cu reprezentri diferite pot avea acelai tip de model, dar a s exist i componente care au asociate mai multe modele: as Model ButtonModel Component a JButton, JToggleButton, JCheckBox, JRadioButton, JMenu, JMenuItem, JCheckBoxMenuItem, JRadioButtomMenuItem JComboBox ComboBoxModel BoundedRangeModel JProgressBar, JScrollBarm, JSlider JTabbedPane SingleSelectionModel ListModel JList ListSelectionModel JList JTable TableModel JTable TableColumnModel JTree TreeModel JTree TreeSelectionModel Document JEditorPane, JTextPane, JTextArea, JTextField, JPasswordField

Fiecare component are un model initial implicit, a are posibilitatea a ns de a-l nlocui cu unul nou atunci cnd este cazul. Metodele care acceseaz a a

312

CAPITOLUL 11. SWING

modelul unui obiect sunt: setModel, respectiv getModel, cu argumente specice ecrei componente parte. Crearea unei clase care s reprezinte a n a un model se va face extinznd interfata corespunztoare i implementnd a a s a metodele denite de aceasta sau extinznd clasa implicit oferit de API-ul a a a Swing i supradenind metodele care ne intereseaz. Pentru modelele mai s a complexe, cum ar cele asociate claselor JTable, JTree sau JList exist a clase abstracte care implementeaz interfata ce descrie modelul respectiv De a exemplu, interfata model a clasei JList este ListModel care este imple mentat de clasele DefaultListModel i AbstractListModel. In functie a s de necesiti, oricare din aceste clase poate extins pentru a crea un nou at a model. Listing 11.5: Folosirea mai multor modele pentru o componenta
import import import import javax . swing .*; javax . swing . border .*; java . awt .*; java . awt . event .*;

class Fereastra extends JFrame implements ActionListener { String data1 [] = { " rosu " , " galben " , " albastru " }; String data2 [] = { " red " , " yellow " , " blue " }; int tipModel = 1; JList lst ; ListModel model1 , model2 ; public Fereastra ( String titlu ) { super ( titlu ) ; s e tD e f au l t C lo s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ; // Lista initiala nu are nici un model lst = new JList () ; getContentPane () . add ( lst , BorderLayout . CENTER ) ; // La apasara butonului schimbam modelul JButton btn = new JButton ( " Schimba modelul " ) ; getContentPane () . add ( btn , BorderLayout . SOUTH ) ; btn . addActionListene r ( this ) ; // Cream obiectele corespunzatoare celor doua modele model1 = new Model1 () ; model2 = new Model2 () ; lst . setModel ( model1 ) ;

11.5. FOLOSIREA MODELELOR

313

pack () ; } public void actionPerformed ( ActionEvent e ) { if ( tipModel == 1) { lst . setModel ( model2 ) ; tipModel = 2; } else { lst . setModel ( model1 ) ; tipModel = 1; } } // Clasele corespunzatoare celor doua modele class Model1 extends Abst ractLis tModel { public int getSize () { return data1 . length ; } public Object getElementAt ( int index ) { return data1 [ index ]; } } class Model2 extends Abst ractLis tModel { public int getSize () { return data2 . length ; } public Object getElementAt ( int index ) { return data2 [ index ]; } } }

public class TestModel { public static void main ( String args []) { new Fereastra ( " Test Model " ) . show () ; } }

Multe componente Swing furnizeaz metode care s obtin starea obieca a a tului fr a mai nevoie s obtinem instanta modelului i s apelm metodele aa a s a a

314

CAPITOLUL 11. SWING

acesteia. Un exemplu este metoda getValue a clasei JSlider care este de fapt un apel de genul getModel().getValue(). In multe situatii a, mai ns ales pentru clase cum ar JTable sau JTree, folosirea modelelor aduce exibilitate sporit programului i este recomandat utilizarea lor. a s a

11.5.1

Tratarea evenimentelor

Modelele componentelor trebuie s notice aparitia unor schimbri ale datelor a a gestionate astfel at s poat reactualizat prezentarea lor sau s e exnc a a a a ecutat un anumti cod cadrul unui obiect de tip listener. In Swing, aceast n a noticare este realizat dou moduri: a n a 1. Informativ (lightweight) - Modelele trimit un eveniment prin care sunt informati asculttorii c a survenit o anumit schimbare a datelor, fr a a a aa a include eveniment detalii legate de schimbarea survenit. Obiectele n a de tip listener vor trebui s apeleze metode specice componentelor pena tru a aa ce anume s-a schimbat. Acest lucru se realizeaz prin intefata a ChangeListener iar evenimentele sunt de tip ChangeEvent, modelele care suport aceast abordare ind BoundedRangeModel, ButtonModel i a a s SingleSelectionModel.
Model BoundedRangeModel ButtonModel SingleSelectionModelModel Listener ChangeListener ChangeListener ChangeListener Tip Eveniment ChangeEvent ChangeEvent ChangeEvent

Interfata ChangeListener are o singur metod: a a public void stateChanged(ChangeEvent e), singura informatie continut eveniment ind componenta surs. a n a Inregistrarea i eliminarea obiectelor de tip listener se realizeaz cu metodele s a addChangeListener, respectiv removeChangeListener. JSlider slider = new JSlider(); BoundedRangeModel model = slider.getModel(); model.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { // Sursa este de tip BoundedRangeModel BoundedRangeModel m = (BoundedRangeModel)e.getSource(); // Trebuie sa interogam sursa asupra schimbarii

11.5. FOLOSIREA MODELELOR

315

System.out.println("Schimbare model: " + m.getValue()); } }); Pentru uurinta programrii, pentru a nu lucra direct cu instanta modelus a lui, unele clase permit nregistrarea asculttorilor direct pentru componenta a sine, singura diferent fat de varianta anterioar constnd faptul c n a a a a n a sursa evenimentului este acum de tipul componentei i nu de tipul modelului. s Secventa de cod de mai sus poate rescris astfel: a JSlider slider = new JSlider(); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { // Sursa este de tip JSlider JSlider s = (JSlider)e.getSource(); System.out.println("Valoare noua: " + s.getValue()); } }); 2. Consistent(statefull) - Modele pun la dispozitie interfete special izate i tipuri de evenimente specice ce includ toate informatiile legate de s schimbarea datelor.
Model ListModel ListSelectionModel ComboBoxModel TreeModel TreeSelectionModel TableModel TableColumnModel Document Document Listener ListDataListener ListSelectionListener ListDataListener TreeModelListener TreeSelectionListener TableModelListener TableColumnModelListener DocumentListener UndoableEditListener Tip Eveniment ListDataEvent ListSelectionEvent ListDataEvent TreeModelEvent TreeSelectionEvent TableModelEvent TableColumnModelEvent DocumentEvent UndoableEditEvent

Folosirea acestor interfete nu difer cu nimic de cazul general: a String culori[] = {"rosu", "galben", "albastru"); JList list = new JList(culori); ListSelectionModel sModel = list.getSelectionModel(); sModel.addListSelectionListener(

316

CAPITOLUL 11. SWING

new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { // Schimbarea este continuta in eveniment if (!e.getValueIsAdjusting()) { System.out.println("Selectie curenta: " + e.getFirstIndex()); } } });

11.6

Folosirea componentelor

Datorit complexitii modelului Swing, aceast secttiune nu vom a at n a ncerca o abordare exhaustiv a modului de utilizare a tuturor componentelor, ci vom a pune evident doar aspectele specice acestui model, subliniind diferentele n a i s mbuntirile fat AWT. a at a

11.6.1

Componente atomice

In categoria componentelor atomice includem componentele Swing cu functionalitate simpl, a cror folosire este general asemntoare cu a echivalentelor din a a n a a AWT. Aici includem: Etichete: JLabel Butoane simple sau cu dou stri: JButton, JCheckBox, JRadioButton, a a JToggleButton; mai multe butoane radio pot grupate folosind clasa ButtonGroup, pentru a permite selectarea doar a unuia dintre ele. Componente pentru progres i derulare: JSlider, JProgressBar, JScrollBar s Separatori: JSeparator Deoarece utilizarea acestora este general facil, nu vom analiza parte n a n aceste componente.

11.6.2

Componente pentru editare de text

Componentele Swing pentru aarea i editarea textelor sunt grupate s s ntr-o ierarhie ce are ca rdcin clasa JTextComponent din pachetul javax.swing.text. a a a

11.6. FOLOSIREA COMPONENTELOR

317

Dup cum se observ din imaginea de mai sus, clasele pot artite a a mp n trei categorii, corespunztoare tipului textului editat: a Text simplu pe o singur linie a JTextField - Permite editarea unui text simplu, pe o singur a linie. JPasswordField - Permite editarea de parole. Textul acestora va ascuns, locul caracterelor introduse ind aat un caracter n s simbolic, cum ar *. JFormattedTextField - Permite introducerea unui text care s a respecte un anumit format, ind foarte util pentru citirea de a numere, date calendaristice, etc. Este folosit a mpreun cu clase a utilitare pentru formatarea textelor, cum ar NumberFormatter, DateFormatter, MaskFormatter, etc. Valoarea continut de o a astfel de component va obtinut/setat cu metodele getValue, a a a respectiv setValue i nu cu cele uzuale getText, setText. s Text simplu pe mai multe linii JTextArea - Permite editarea unui text simplu, pe mai multe linii. Orice atribut legat de stil, cum ar culoarea sau fontul, se aplic a ntregului text i nu poate specicat doar unei anumite s portiuni. Uzual, o component de acest tip va inclus a a ntr-un container JScrollPane, pentru a permite navigarea pe vertical a

318

CAPITOLUL 11. SWING i orizontal dac textul introdus nu s a a ncape suprafata alocat n a obiectului. Acest lucru este valabil pentru toate componentele Swing pentru care are sens notiunea de navigare pe orizontal a sau vertical, nici una neoferind suport intrinsec pentru aceast a a operatiune.

Text cu stil mbogit pe mai multe linii at JEditorPane - Permite aarea i editarea de texte scrise cu stils s uri multiple i care pot include imagini sau chiar diverse alet coms ponente. Implicit, urmtoarele tipuri de texte sunt recunoscute: a text/plain, text/html i text/rtf. Una din utilizrile cele mai s a simple ale acestei clase este setarea documentului ce va aat s cu metoda setPage, ce primete ca argument un URL care poate s referi un ier text, HTML sau RTF. s JTextPane - Aceast clas extinde JEditorPane, oferind diverse a a faciliti suplimentare pentru lucrul cu stiluri i paragrafe. at s Clasa JTextComponent ncearc s pstreze ct mai multe similitudini cu a a a a clasa TextComponent din AWT, a exist diferente notabile ns a ntre cele dou, a componenta Swing avnd caracteristici mult mai complexe cum ar suport a pentru operatii de undo i redo, tratarea evenimentelor generate de cursor s (caret), etc. Orice obiect derivat din JTextComponent este format din: Un model, referit sub denumirea de document, care gestioneaz starea a componentei. O referint la model poate obtinut cu metoda getDocument, a a ce returneaz un obiect de tip Document. a O reprezentare, care este responsabil cu aarea textului. a s Un controller, cunoscut sub numele de editor kit care permite scrierea i citirea textului i care permite denirea de actiuni necesare editrii. s s a Exist diferent fat de AWT i la nivelul tratrii evenimentelor generate a a s a de componentele pentru editarea de texte. Dintre evenimentele ce pot generate amintim: ActionEvent - Componentele derivate din JTextField vor genera un eveniment de acest tip la apsarea tastei Enter csuta de editare a a n a textului. Interfata care trebuie implementat este ActionListener. a

11.6. FOLOSIREA COMPONENTELOR

319

CaretEvent - Este evenimentul generat la deplasarea cursorului ce gestioneaz pozitia curent text. Interfata corespunztoare CaretLisa a n a tener contine o singur metod: caretUpdate ce va apelat ori de a a a cte ori apare o schimbare. a

DocumentEvent - Evenimentele de acest tip sunt generate la orice schimbare a textului, sursa lor ind documentul (modelul) componentei i nu componenta sine. Interfata corespunztoare este Docus n a mentListener, ce contine metodele:

insertUpdate - apelat la adugarea de noi caractere; a a removeUpdate - apelat dup o operatiune de tergere; a a s changedUpdate - apelat la schimbarea unor atribute legate de a stilul textului.

PropertyChangeEvent - Este un eveniment comun tuturor componentelor de tip JavaBean, ind generat la orice schimbare a unei proprieti a componentei. Interfata corespunztoare este Propertyat a ChangeListener, ce contine metoda propertyChange.

11.6.3

Componente pentru selectarea unor elemente

In aceast categorie vom include clasele care permit selectarea unor valori a (elemente) dintr-o serie prestabilit. Acestea sunt: JList, JComboBox i a s JSpinner.

Clasa JList Clasa JList descrie o list de elemente dispuse pe una sau mai multe coloane, a din care utilizatorul poate selecta unul sau mai multe. Uzual un obiect de acest tip va inclus ntr-un container de tip JScrollPane.

320

CAPITOLUL 11. SWING

Initializarea unei liste se realizeaz mai multe modaliti: a n at Folosind unul din constructorii care primesc ca argument un vector de elemente. Object elemente[] = {"Unu", "Doi", new Integer(3), new Double(4)}; JList lista = new JList(elemente); Folosind constructorul fr argumente i adugnd apoi elemente modaa s a a elului implicit listei: DefaultListModel model = new DefaultListModel(); model.addElement("Unu"); model.addElement("Doi"); model.addElement(new Integer(3)); model.addElement(new Double(4)); JList lista = new JList(model); Folosind un model propriu, responsabil cu furnizarea elementelor listei. Acesta este un obiect dintr-o clas ce trebuie s implementeze a a interfata ListModel, uzual ind folosit extinderea clasei predenite a AbstractListModel i supradenirea metodelor: getElementAt care s furnizeaz elementul de pe o anumit positie din list, respectiv getSize a a a care trebuie s returneze numrul total de elemente din list. Evident, a a a aceast variant este mai complex, oferind exibilitate sporit lua a a a n crul cu liste. ModelLista model = new ModelLista(); JList lista = new JList(model);

11.6. FOLOSIREA COMPONENTELOR

321

... class ModelLista extends AbstractListModel { Object elemente[] = {"Unu", "Doi", new Integer(3), new Double(4)}; public int getSize() { return elemente.length; } public Object getElementAt(int index) { return elemente[index]; } }

Gestiunea articolelor selectate dintr-o list se realizeaz prin intermediul a a unui model, acesta ind un obiect de tip ListSelectionModel. Obiectele de tip JList genereaz evenimente de tip ListSelectionEvent, interfata corea spunztoare ind ListSelectionListener ce contine metoda valueChanged a apelat ori de cte ori va schimbat selectia elementelor din list. a a a a class Test implements ListSelectionListener { ... public Test() { ... // Stabilim modul de selectie list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); /* sau SINGLE_INTERVAL_SELECTION MULTIPLE_INTERVAL_SELECTION */ // Adaugam un ascultator ListSelectionModel model = list.getSelectionModel(); model.addListSelectionListener(this); ... } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; int index = list.getSelectedIndex();

322 ... } }

CAPITOLUL 11. SWING

Evident, clasa ofer metode pentru selectarea unor elemente din cadrul a programului setSelectedIndex, setSelectedIndices, etc. i pentru obtinerea s celor selectate la un moment dat getSelectedIndex, getSelectedIndices, etc..

O facilitate extrem de important pe care o au listele este posibilitatea de a a stabili un renderer pentru ecare articol parte. Implicit toate elementele n listei sunt aate acelai fel, a acest lucru poate schimbat prin crearea s n s ns unei clase ce implementeaz interfata ListCellRenderer i personalizeaz a s a reprezentarea elementelor listei functie de diveri parametri. Interfata n s ListCellRenderer contine o singur metod getListCellRendererCom a a ponent ce returneaz un obiect de tip Component. Metoda va apelat a a n parte pentru reprezentarea ecrui element al listei. a class MyCellRenderer extends JLabel implements ListCellRenderer { public MyCellRenderer() { setOpaque(true); } public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setText(value.toString()); setBackground(isSelected ? Color.red : Color.white); setForeground(isSelected ? Color.white : Color.black); return this; } } Setarea unui anumit renderer pentru o list se realizeaz cu metoda setCella a Renderer.

Clasa JComboBox Clasa JComboBox este similar cu JList, cu deosebirea c permite doar sea a lectarea unui singur articol, acesta ind i singurul permanent vizibil. Lista s

11.6. FOLOSIREA COMPONENTELOR

323

celorlalte elemente este aat doar la apsarea unui buton marcat cu o s a a sgeat, ce face parte integrant din component. a a a a

JComboBox functioneaz dup aceleai principii ca i clasa JList. a a s s Initializarea se face dintr-un vector sau folosind un model de tipul Com boBoxModel, ecare element putnd de asemenea reprezentat diferit prin a intermediul unui obiect ce implementeaz aceeai intefat ca i cazul lisa s a s n telor: ListCellRenderer. O diferent notabil const modul de selectare a unui articol, deoarece a a a n JComboBox permite i editarea explicit a valorii elementului, acest lucru ind s a controlat de metoda setEditable. Evenimentele generate de obiectele JComboBox sunt de tip ItemEvent generate la navigarea prin list, respectiv ActionEvent generate la selectarea a efectiv a unui articol. a

Clasa JSpinner Clasa JSpinner ofer posibilitatea de a selecta o anumit valoare (element) a a dintr-un domeniu prestabilit, lista elementelor neind a vizibil. Este ns a folosit atunci cnd domeniul din care poate fcut selectia este foarte mare a a a sau chiar nemrginit; de exemplu: numere intregi intre 1950 si 2050. Coma ponenta contine dou butoane cu care poate selectat urmtorul, respectiv a a predecesorul element din domeniu.

JSpiner se bazeaz exclusiv pe folosirea unui model. Acesta este un a obiect de tip SpinnerModel, existnd o serie de clase predenite ce implea menteaz aceast interfat cum ar SpinnerListModel, SpinnerNumberModel a a a sau SpinnerDateModel ce pot utilizate.

324

CAPITOLUL 11. SWING

Componentele de acest tip permit i specicarea unui anumit tip de s editor pentru valorile elementelor sale. Acesta este instalat automat pentru ecare din modelele standard amintite mai sus, ind reprezentat de una din clasele JSpinner.ListEditor, JSpinner.NumberEditor, respectiv JSpinner.DateEditor, toate derivate din JSpinner.DefaultEditor. Fiecare din editoarele amintite permite diverse formatri specice. a Evenimentele generate de obiectele de tip JSpinner sunt de tip ChangeEvent, generate la schimbarea strii componentei. a

11.6.4

Tabele

Clasa JTable permite crearea de componente care s aeze o serie de elea s mente ntr-un format tabelar, articolele ind dispuse pe linii i coloane. Un s tabel poate folosit doar pentru aarea formatat a unor date, dar este s a posibil i editarea informatiei din celulele sale. De asemenea, liniile tabeluas lui pot marcate ca selectate, tipul selectiei ind simplu sau compus, tabelele extinznd astfel functionalitatea listelor. a

Dei clasa JTable se gsete pachetul javax.swing, o serie de clase i s a s n s interfete necesare lucrului cu tabele se gsesc pachetul javax.swing.table, a n acesta trebuind aadar importat. s Initializarea unui tabel poate fcut mai multe moduri. a a n Cea mai simpl variant este s folosim unul din constructorii care primesc a a a ca argumente elementele tabelului sub forma unei matrici sau a unei colectii de tip Vector i denumirile capurilor de coloan: s a String[] coloane = {"Nume", "Varsta", "Student"}; Object[][] elemente = { {"Ionescu", new Integer(20), Boolean.TRUE}, {"Popescu", new Integer(80), Boolean.FALSE}}; JTable tabel = new JTable(elemente, coloane); Dup cum se observ, tipul de date al elementelor de pe o coloan este a a a de tip referint i poate oricare. In cazul care celulele tabelului sunt a s n

11.6. FOLOSIREA COMPONENTELOR

325

editabile trebuie s existe un editor potrivit pentru tipul elementului din a celula respectiv. Din motive de ecient, implementarea acestei clase este a a orientat la nivel de coloan, ceea ce a a nseamn c articole de pe o coloan a a a vor reprezentate la fel i vor avea acelai tip de editor. s s A doua variant de creare a unui tabel este prin implementarea modelua lui acestuia ntr-o clas separat i folosirea constructorului corespunztor. a as a Interfata care descrie modelul clasei JTable este TableModel i contine s metodele care vor interogate pentru obtinerea informatiei din tabel. Uzual, crearea unui model se face prin extinderea clasei predenite AbstractTableModel, care implementeaz deja TableModel. Tot ceea ce trebuie s facem a a este s supradenim metodele care ne intereseaz, cele mai utilizate ind a a (primele trei trebuie obligatoriu supradenite, ele ind declarate abstracte n clasa de baz): a getRowCount - returneaz numrul de linii ale tabelului; a a getColumnCount - returneaz numrul de coloane ale tabelului; a a getValueAt - returneaz elementul de la o anumit linie i coloan; a a s a getColumnName - returneaz denumirea ecrei coloane; a a isCellEditable - specic dac o anumit celul este editabil. a a a a a Modelul mai contine i metoda setValueAt care poate folosit pentru s a setarea explicit a valorii unei celule. a ModelTabel model = new ModelTabel(); JTable tabel = new JTable(model); ... class ModelTabel extends AbstractTableModel { String[] coloane = {"Nume", "Varsta", "Student"}; Object[][] elemente = { {"Ionescu", new Integer(20), Boolean.TRUE}, {"Popescu", new Integer(80), Boolean.FALSE}}; public int getColumnCount() { return coloane.length; } public int getRowCount() {

326 return elemente.length;

CAPITOLUL 11. SWING

} public Object getValueAt(int row, int col) { return elemente[row][col]; } public String getColumnName(int col) { return coloane[col]; } public boolean isCellEditable(int row, int col) { // Doar numele este editabil return (col == 0); } }

Orice schimbare a datelor tabelului va genera un eveniment de tip TableModelEvent. Pentru a trata aceste evenimente va trebui s implementm a a interfata TableModelListener ce contine metoda tableChanged. Inreg istrarea unui listener va fcut pentru modelul tabelului: a a

public class Test implements TableModelListener { ... public Test() { ... tabel.getModel().addTableModelListener(this); ... } public void tableChanged(TableModelEvent e) { // Aflam celula care a fost modificata int row = e.getFirstRow(); int col = e.getColumn(); TableModel model = (TableModel)e.getSource(); Object data = model.getValueAt(row, col); ... } }

11.6. FOLOSIREA COMPONENTELOR

327

Tabele ofer posibilitatea de a selecta una sau mai multe linii, nu neaprat a a consecutive, gestiunea liniilor selectate ind realizat prin intermediul unui a model. Acesta este o instant ce implementeaz, a a ntocmai ca la liste, interfata ListSelectionModel. Tratarea evenimentelor generate de schimbarea selectiei tabel se realizeaz prin n a nregistrarea unui asculttor de tip ListSelectiona Listener: class Test implements ListSelectionListener { ... public Test() { ... // Stabilim modul de selectie tabel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // Adaugam un ascultator ListSelectionModel model = tabel.getSelectionModel(); model.addListSelectionListener(this); ... } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; ListSelectionModel model = (ListSelectionModel)e.getSource(); if (model.isSelectionEmpty()) { // Nu este nici o linie selectata ... } else { int index = model.getMinSelectionIndex(); // Linia cu numarul index este prima selectata ... } } }

Dup cum am spus, celule unei coloane vor reprezentare la fel, ecare a coloan avnd asociat un obiect renderer responsabil cu crearea componena a

328

CAPITOLUL 11. SWING

tei ce descrie celulele sale. Un astfel de obiect implementeaz interfata a TableCellRenderer, care are o singur metod getTableCellRendera a erComponent, aceasta ind responsabil cu crearea componentelor ce vor a aate celulele unei coloane. Implicit, exist o serie de tipuri de date s n a cu reprezentri specice, cum ar : Boolean, Number, Double, Float, a Date, ImageIcon, Icon, restul tipurilor avnd o reprezentare standard ce a const a ntr-o etichet cu reprezentarea obiectului ca ir de caractere. Specia s carea unui renderer propriu se realizeaz cu metoda setDefaultRenderer, a ce asociaz un anumit tip de date cu un obiect de tip TableRenderer. a public class MyRenderer extends JLabel implements TableCellRenderer { public Component getTableCellRendererComponent(...) { ... return this; } }

O situatie similar o regsim la nivelul editorului asociat celulelor dintr-o a a anumit coloan. Acesta este un obiect ce implementeaz interfata Treea a a CellEditor, ce extinde interfata CellEditor care generalizeaz conceptul a de celul editabil pe care vom mai regsi la arbori. Implicit, exist o a a l a a serie de editoare standard pentru tipurile de date mentionate anterior, dar este posibil specicarea unui editor propriu cu metoda setDefaultEditor. a Crearea unui editor propriu se realizeaz cel mai simplu prin extinderea clasei a utilitare AbstractCellEditor, care implementeaz CellEditor, plus implea mentarea metodei specice din TreeCellEditor. public class MyEditor extends AbstractCellEditor implements TableCellEditor { // Singura metoda abstracta a parintelui public Object getCellEditorValue() { // Returneaza valoarea editata ... }

11.6. FOLOSIREA COMPONENTELOR // Metoda definita de TableCellEditor public Component getTableCellEditorComponent(...) { // Returneaza componenta de tip editor ... } }

329

11.6.5

Arbori

Clasa JTree permite aarea unor elemente s ntr-o manier ierarhic. Ca a a orice component Swing netrivial, un obiect JTree reprezint doar o imagine a a a a datelor, informatia sine ind manipulat prin intermediul unui model. n a La nivel structural, un arbore este format dintr-o rdcin, noduri interne a a a care au cel putin un u i noduri frunz - care nu mai au nici un descendent. s a

Dei clasa JTree se gsete pachetul javax.swing, o serie de clase i s a s n s interfete necesare lucrului cu arbori se gsesc pachetul javax.swing.tree. a n Clasa care modeleaz notiunea de nod al arborelui este DefaultMutablea TreeNode, aceasta ind folosit pentru toate tipurile de noduri. Crearea a unui arbore presupune aadar crearea unui nod (rdcina), instantierea unui s a a obiect de tip JTree cu rdcina creat i adugarea apoi de noduri frunz ca a a as a a i ai unor noduri existente. String text = "<html><b>Radacina</b></html>"; DefaultMutableTreeNode root = new DefaultMutableTreeNode(text); DefaultMutableTreeNode numere = new DefaultMutableTreeNode("Numere"); DefaultMutableTreeNode siruri =

330

CAPITOLUL 11. SWING new DefaultMutableTreeNode("Siruri");

for(int i=0; i<3; i++) { numere.add(new DefaultMutableTreeNode(new Integer(i))); siruri.add(new DefaultMutableTreeNode("Sirul " + i)); } root.add(numere); root.add(siruri); JTree tree = new JTree(root); Dup cum se observ, nodurile arborelui pot de tipuri diferite, reprezentarea a a lor implicit ind obtinutprin apelarea metodei toString pentru obiectului a a continut. De asemenea, este posibil specicarea unui text format HTML a n ca valoare a unui nod, acesta ind reprezentat ca atare. Dac varianta adugrii explicite a nodurilor nu este potrivit, se poate a a a a implementa o clas care s descrie modelul arborelui. Aceasta trebuie s a a a implementeze intefata TreeModel. Scopul unei componente de tip arbore este general selectarea unui nod n al ierarhiei. Ca i cazul listelor sau a tabelelor, gestiunea elementelor s n selectate se realizeaz printr-un model, aceast situatie interfata corea n a spunztoare ind TreeSelectionModel. Arborii permit a nregistrarea unor obiecte listener, de tip TreeSelectionListener, care s trateze evenimentele a generate la schimbarea selectiei arbore. n class Test implements TreeSelectionListener { ... public Test() { ... // Stabilim modul de selectie tree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION); // Adaugam un ascultator tree.addTreeSelectionListener(this); ... }

11.6. FOLOSIREA COMPONENTELOR public void valueChanged(TreeSelectionEvent e) { // Obtinem nodul selectat DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (node == null) return; // Obtinem informatia din nod Object nodeInfo = node.getUserObject(); ... } }

331

Fiecare nod al arborelui este reprezentar prin intermediul unei clase renderer. Aceasta implementeaz interfata TreeCellRenderer, cea folosit a a implicit ind DefaultTreeCellRenderer. Prin implementarea interfetei sau extinderea clasei implicite pot create modaliti de personalizare a at nodurilor arborelui functie de tipul sau valoarea acestora. n Exist a i diverse metode de a schimba aiarea unui arbore fr s a ns s nft s aa a crem noi clase de tip TreeCellRenderer. Acestea sunt: a setRootVisible - Specic dac rdcina e vizibil sau nu; a a a a a setShowsRootHandles - Specic dac nodurile de pe primul nivel au a a simboluri care s permit expandarea sau restrngerea lor. a a a putClientProperty - Stabilete diverse proprieti, cum ar modul s at de reprezentare a relatiilor (liniilor) dintre nodurile printe i u: a s tree.putClientProperty("JTree.lineStyle", "Angled"); // sau "Horizontal", "None" Specicarea unei iconite pentru nodurile frunz sau interne: a ImageIcon leaf = createImageIcon("img/leaf.gif"); ImageIcon open = createImageIcon("img/open.gif"); ImageIcon closed = createImageIcon("img/closed.gif");

332

CAPITOLUL 11. SWING

DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer(); renderer.setLeafIcon(leaf); renderer.setOpenIcon(open); renderer.setClosedIcon(closed); tree.setCellRenderer(renderer);

11.6.6

Containere

Dup cum tim, containerele reprezint suprafet de aare pe care pot a s a s plasate ale componente, eventual chiar alte containere. Superclasa componentelor de acest tip este Container, clas despre care am mai discutat a n capitolul dedicat modeluli AWT. Containerele pot artite dou categorii: mo n a 1. Containere de nivel nalt - Acestea sunt JFrame, JDialog, JApplet i reprezint rdcinile ierarhiilor de componente ale unei aplicatii. s a a a 2. Containere intermediare - Reprezint suprafete de aare cu ajua s torul crora pot organizate mai ecient componentele aplicatiei, putnd a a imbricate. Cele mai importante clase care descriu astfel de containere sunt: JPanel JScrollPane JTabbedPane JSplitPane JLayeredPane JDesktopPane JRootPane JPanel are aceeai functionalitate ca i clasa Panel din AWT, ind folosit s s pentru gruparea mai multor componente Swing i plasarea lor s mpreun a pe o alt suprafat de aare. Gestionarul de pozitionare implicit este a a s FlowLayout, acesta putnd schimbat a chiar momentul construirii a ns n

11.6. FOLOSIREA COMPONENTELOR

333

obiectului JPanel sau ulterior cu metoda setLayout. Adugarea de compoa nente se realizeaz ca pentru orice container, folosind metoda add. a JPanel p = new JPanel(new BorderLayout()); /* Preferabil, deoarece nu mai este construit si un obiect de tip FlowLayout (implicit) */ p.add(new JLabel("Hello")); p.add(new JButton("OK")); ...

JScrollPane este o clas foarte important arhitectura modelului a a n Swing, deoarece ofer suport pentru derularea pe orizontal i vertical a a a s a componentelor a cror reprezentare complet nu a a ncape suprafata ason ciat, nici o component Swing neoferind suport intrinsec pentru aceast a a a operatie. String elemente[] = new String[100]; for(int i=0; i<100; i++) elemente[i] = "Elementul " + i; JList lista = new JList(elemente); JScrollPane sp = new JScrollPane(lista); frame.getContentPane().add(sp);

JTabbedPane este util pentru suprapunerea mai multor containere, a uzual panouri (obiecte de tip JPanel), pe acelai spatiu de aare, selectarea s s

334

CAPITOLUL 11. SWING

unuia sau altui panou realizndu-se prin intermediul unor butoane dispuse a pe partea superioar a componentei, ecare panou avnd un astfel de bua a ton corespunztor. Ca functionalitate, ofer o implementare asemntoare a a a a gestionarului de pozitionare CardLayout. JTabbedPane tabbedPane = new JTabbedPane(); ImageIcon icon = new ImageIcon("smiley.gif"); JComponent panel1 = new JPanel(); panel1.setOpaque(true); panel1.add(new JLabel("Hello")); tabbedPane.addTab("Tab 1", icon, panel1, "Aici avem o eticheta"); tabbedPane.setMnemonicAt(0, KeyEvent.VK_1); JComponent panel2 = new JPanel(); panel2.setOpaque(true); panel2.add(new JButton("OK")); tabbedPane.addTab("Tab 2", icon, panel2, "Aici avem un buton"); tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);

JSplitPane permite crearea unui container care contine dou compo a nente dispuse e una lng cealalt, e una sub alta i separarea acestora a a a s prin intermediul unei bare care s permit congurarea suprafetei alocate a a ecrei componente. a String elem[] = {"Unu", "Doi", "Trei" }; JList list = new JList(elem);

11.6. FOLOSIREA COMPONENTELOR JPanel panel = new JPanel(new GridLayout(3, 1)); panel.add(new JButton("Adauga")); panel.add(new JButton("Sterge")); panel.add(new JButton("Salveaza")); JTextArea text = new JTextArea( "Mai multe componente separate prin\n" + "intermediul containerelor JSplitPane"); // Separam lista de grupul celor trei butoane JSplitPane sp1 = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, list, panel); // Separam containerul cu lista si butoanele // de componenta pentru editare de text JSplitPane sp2 = new JSplitPane( JSplitPane.VERTICAL_SPLIT, sp1, text); frame.getContentPane().add(sp2);

335

11.6.7

Dialoguri

Clasa care descrie ferestre de dialog este JDialog, crearea unui dialog realizndu-se prin extinderea acesteia, a ntocmai ca modelul AWT. In Swing n exist a o serie de clase predenite ce descriu anumite tipuri de dialoguri, a ns extrem de utile majoritatea aplicatiilor. Acestea sunt: n JOptionPane - Permite crearea unor dialoguri simple, folosite pentru aarea unor mesaje, realizarea unor interogri de conrmare/renuntare, s a

336

CAPITOLUL 11. SWING etc. sau chiar pentru introducerea unor valori, clasa ind extrem de congurabil. Mai jos, sunt exemplicate dou modaliti de utilizare a a at a clasei: JOptionPane.showMessageDialog(frame, "Eroare de sistem !", "Eroare", JOptionPane.ERROR_MESSAGE); JOptionPane.showConfirmDialog(frame, "Doriti inchiderea aplicatiei ? ", "Intrebare", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

JFileChooser - Dialog standard care permite navigarea prin sistemul de iere i selectarea unui anumit ier pentru operatii de deschidere, s s s respectiv salvare. JColorChooser - Dialog standard pentru selectarea ntr-o manier a facil a unei culori. a ProgressMonitor - Clas utilizat pentru monitorizare progresului a a a unei operatii consumatoare de timp.

11.7
11.7.1

Desenarea
Metode specice

Dup cum tim, desenarea unei componente este un proces care se executa a s automat ori de cte ori este necesar. Procesul sine este asemntor celui a n a a din modelul AWT, a exist unele diferente care trebuie mentionate. ns a Orice component se gsete a a s ntr-o ierarhie format de containere, rdcina a a a acestei ind un container de nivel nalt, cum ar o fereastr sau suprafata a unui applet. Cu alte cuvinte, componenta este plasat pe o suprafat de a a aare, care la rndul ei poate plasat pe alt suprafat i aa mai departe. s a a a a s s Cnd este necesar desenarea componentei repsective, e la prima sa aare, a a s e ca urmare a unor actiuni externe sau interne programului, operatia de de senare va executat pentru toate containerele, a ncepnd cu cel de la nivelul a superior.

11.7. DESENAREA

337

Desenarea se bazeaz pe modelul AWT, metoda cea mai important ind a a paint, apelat automat ori de cte ori este necesar. Pentru componentele a a Swing, aceast metod are a o implementare specic i nu trebuie a a ns a s supradenit. Aceasta este responsabil cu apelul metodelor Swing ce a a deseneaz componenta i anume: a s paintComponent - Este principala metod pentru desenare ce este a supradenit pentru ecare component Swing parte pentru a descrie a a n reprezentarea sa grac. Implicit, cazul care componenta este a n n opac metoda deseneaz suprafata sa cu culoarea de fundal, dup care a a a va executa desenarea propriu-zis. a paintBorder - Deseneaz chenarele componentei (dac exist). Nu a a a trebuie supradenit. a paintChildren - Solicit desenarea componentelor continute de aceast a a component (dac exist). Nu trebuie supradenit. a a a a Metoda paint este responsabil cu apelul metodelor amintite mai sus i a s realizarea unor optimizri legate de procesul de desenare, cum ar implea mentarea mecanismului de double-buering. Dei este posibil supradenirea s a ei, acest lucru nu este recomandat, din motivele amintite mai sus. Ca i AWT, dac se dorete redesenarea explicit a unei componente s n a s a se va apela metoda repaint. In cazul care dimensiunea sau pozitia comn ponentei s-au schimbat, apelul metodei revalidate va precede apelul lui repaint.

Atentie Intocmai ca AWT, desenarea este realizat de rul de executie care se n a ocup cu transmiterea evenimentelor. Pe perioada care acesta este ocupat a n cu transmiterea unui mesaj nu va fcut nici o desenare. De asemenea, a a dac acesta este blocat a ntr-o operatiune de desenare ce consum mult timp, a pe perioada respectiv nu va transmis nici un mesaj. a

338

CAPITOLUL 11. SWING

11.7.2

Consideratii generale

In continuare vom prezenta cteva consideratii generale legate de diferite a aspecte ale desenrii cadrul modelului Swing. a n

Aarea imaginilor s In AWT aarea unei imagini era realizat uzual prin supradenirea clasei s a Canvas i desenarea imaginii metoda paint a acesteia. In Swing, exist s n a cteva solutii mai simple pentru aarea unei imagini, cea mai utilizat ind a s a crearea unei etichete (JLabel) sau a unui buton (JButton) care s aib sea a tat o anumit imagine pe suprafata sa. Imaginea respectiv trebuie creat a a a a folosind clasa ImageIcon. ImageIcon img = new ImageIcon("smiley.gif"); JLabel label = new JLabel(img);

Transparenta Cu ajutorul metodei setOpaque poate controlat opacitatea componentelor a Swing. Aceasta este o facilitate extrem de important deoarece permite a crearea de componente care nu au form rectangular. De exemplu, un a a buton circular va construit ca ind transparent (setOpaque(false)) i va s desena interiorul su o elips umplut cu o anumit culoare. Evident, este n a a a a necesar implementarea de cod specic pentru a trata apsarea acestui tip a a de buton. Trabsparenta a vine cu un anumit pret, deoarece pentru componentele ns transparente vor trebui redesenate containerele pe care se gsete aceasta, a s ncetinind astfel procesul de aare. Din acest motiv, de ecare dat cnd s a a este cazul, se recomand setarea componentelor ca ind opace a (setOpaque(true)).

Dimensiunile componentelor Dup cum tim, orice component este denit de o suprafat rectangua s a a a lar. Dimensiunile acestei pot obtinute cu metodele getSize, getWidth, a getHeight. Acestea includ a i dimsniunile chenarelor, evident dac acesns s a tea exist. Suprafata ocupat de acestea poate aat cu metoda getInsets a a a

11.7. DESENAREA

339

ce va returna un obiect de tip Insets ce specic numrul de pixeli ocupati a a cu chenare jurul componentei. n public void paintComponent(Graphics g) { ... Insets insets = getInsets(); int currentWidth = getWidth() - insets.left - insets.right; int currentHeight = getHeight() - insets.top - insets.bottom; ... }

Contexte grace Argumentul metodei paintComponent este de tip Graphics ce ofer prima itivele standard de desenare. In majoritatea cazurilor a, argumentul este ns de fapt de tip Graphics2D, clas ce extinde Graphics i pune la dispozitie a s metode mai sositcate de desenare cunoscute sub numele de Java2D. Pentru a avea acces la API-ul Java2D, este sucient s facem conversia argumentului a ce descrie contextul grac: public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D)g; // Desenam apoi cu g2d ... } In Swing, pentru a ecientiza desenarea, obiectul de tip Graphics primit ca argument de metoda paintComponent este refolosit pentru desenarea componentei, a chenarelor i a ilor si. Din acest motiv este foarte important s a ca atunci cnd supradenim metoda paintComponent s ne asigurm c la a a a a terminarea metodei starea obiectului Graphics este aceeai ca la s nceput. Acest lucru poate realizat e explicit, e folosind o copie a contextului grac primit ca argument: // 1.Explicit Graphics2D g2d = (Graphics2D)g; g2d.translate(x, y); // modificam contexul ... g2d.translate(-x, -y); // revenim la starea initiala

340

CAPITOLUL 11. SWING

// 2. Folosirea unei copii Graphics2D g2d = (Graphics2D)g.create(); g2d.translate(x, y); ... g2d.dispose();

11.8

Look and Feel

Prin sintagma Look and Feel (L&F) vom elege modul care sunt dent n senate componentele Swing i felul care acestea interactioneaz cu utis n a lizatorul. Posibilitatea de a alege ntre diferite moduri L&F are avantajul de a oferi prezentarea unei aplicatii ntr-o form grac care s corespund a a a a preferintelor utilizatorilor. In principiu, variantele originale de L&F furnizate distributia standard ofereau modalitatea ca o interfat Swing e s se n a a ncadreze ansamblul grac al sistemului de operare folosit, e s aib un n a a aspect specic Java. Orice L&F este descris de o clas derivat din LookAndFeel. Distributia a a standard Java include urmtoarele clase ce pot utilizate pentru selectarea a unui L&F: javax.swing.plaf.metal.MetalLookAndFeel Este varianta implicit de L&F i are un aspect specic Java. a s com.sun.java.swing.plaf.windows.WindowsLookAndFeel Varianta specic sistemelor de operare Windows. Incepnd cu versia a unea 1.4.2 exist i implementarea pentru Windows XP . as com.sun.java.swing.plaf.mac.MacLookAndFeel Varianta specic sistemelor de operare Mac. a com.sun.java.swing.plaf.motif.MotifLookAndFeel Specic interfata CDE/Motif. a com.sun.java.swing.plaf.gtk.GTKLookAndFeel GTK+ reprezint un standard de creare a interfetelor grace dezvoltat a independent de limbajul Java. (GTK este acronimul de la GNU Image Manipulation Program Toolkit). Folosind acest L&F este posibil i a s

11.8. LOOK AND FEEL

341

specicarea unei anumite teme prin intermediul unui ier de resurse s sau folosind variabila swing.gtkthemefile, ca exemplul de mai jos: n java -Dswing.gtkthemefile=temaSpecifica/gtkrc App Specicare unei anumite interfete L&F poate realizat prin mai multe a modaliti. at

Folosirea clasei UImanager Clasa UIManager pune la dispozitie o serie de metode statice pentru se lectarea la momentul executiei a uni anumit L&F, precum i pentru obtinerea s unor variante specice: getLookAndFeel - Obtine varianta curent, returnnd un obiect de tip a a LookAndFeel. setLookAndFeel - Seteaz modul curet L&F. Metoda primete ca ara s gument un obiect dintr-o clas derivat din LookAndFeel, e un ir de a a s caractere cu numele complet al clasei L&F. getSystemLookAndFeelClassName - Obtine variant specic sistemu a a lui de operare folosit. In cazul care nu exist nici o astfel de clas, n a a returneaz varianta standard. a getCrossPlatformLookAndFeelClassName - Returneaz interfata grac a a standard Java (JLF). // Exemple: UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());

Setarea proprietii swing.defaultlaf at Exist posibilitatea de a specica varianta de L&F a aplicatie direct de la a linia de comand prin setarea proprietii swing.defaultlaf: a at

342

CAPITOLUL 11. SWING

java -Dswing.defaultlaf= com.sun.java.swing.plaf.gtk.GTKLookAndFeel App java -Dswing.defaultlaf= com.sun.java.swing.plaf.windows.WindowsLookAndFeel App O alt variant de a seta aceast proprietate este schimbarea ei direct a a a n ierul swing.properties situat subdirectorul lib al distributiei Java. s n # Swing properties swing.defaultlaf= com.sun.java.swing.plaf.windows.WindowsLookAndFeel Ordinea care este aleas clasa L&F este urmtoarea: n a a 1. Apelul explicit al metodei UIManager.setLookAndFeel naintea crerii a unei componente Swing. 2. Proprietatea swing.defaultlaf specicat de la linia de comand. a a 3. Proprietatea swing.defaultlaf specicat ierul swing.properties. a n s 4. Clasa standard Java (JLF).

Exist posibilitatea de a schimba varianta de L&F chiar i dup aarea a s a s componentelor. Acesta este un proces care trebuie s actualizeze ierarhiile a de componente, ncepnd cu containerele de nivel a nalt i va realizat prin s apelul metodei SwingUtilities.updateComponentTreeUI ce va primi ca argument rdcina unei ierarhii. Secventa care efectueaz aceast operatie a a a a pentru o fereastr f este: a UIManager.setLookAndFeel(numeClasaLF); SwingUtilities.updateComponentTreeUI(f); f.pack();

Capitolul 12 Fire de executie


12.1 Introducere

Firele de executie fac trecerea de la programarea secvential la programarea a concurent. Un program secvential reprezint modelul clasic de program: are a a un nceput, o secvent de executie a instructiunilor sale i un sfrit. Cu alte a s as cuvinte, la un moment dat programul are un singur punct de executie. Un program aat executie se numete proces. Un sistem de operare monotaskn s ing, cum ar MS-DOS, nu este capabil s execute dect un singur proces a a la un moment dat, timp ce un sistem de operare multitasking, cum ar n UNIX sau Windows, poate rula oricte procese acelai timp (concurent), a n s folosind diverse strategii de alocare a procesorului ecruia dintre acestea. a Am reamintit acest lucru deoarece notiunea de r de executie nu are sens dect cadrul unui sistem de operare multitasking. a n Un r de executie este similar unui proces secvential, sensul c are n a un nceput, o secvent de executie i un sfrit. Diferenta dintre un r de a s as executie i un proces const faptul c un r de executie nu poate rula s a n a independent ci trebuie s ruleze cadrul unui proces. a n Denitie Un r de executie este o succesiune scevential de instructiuni care se a execut cadrul unui proces. a n

343

344 Program (proces)

CAPITOLUL 12. FIRE DE EXECUTIE Program (proces)

Un program si poate deni a nu doar un r de executie ci oricte, ceea ns a ce nseamn c cadrul unui proces se pot executa simultan mai multe re a a n de executie, permitnd efectuarea concurent a sarcinilor independente ale a a acelui program. Un r de executie poate asemnat cu o versiune redus a unui proces, a a ambele rulnd simultan i independent pe o structur secvential format de a s a a a instructiunile lor. De asemenea, executia simultan a relor cadrul unui a n proces este similar cu executia concurent a proceselor: sistemul de operare a a va aloca procesorul dup o anumit strategie ecrui r de executie pn la a a a a a terminarea lor. Din acest motiv rele de executie mai sunt numite i procese s usoare. Care ar a deosebirile ns ntre un r de executie i un proces ? In primul, s rnd deosebirea major const faptul c rele de executie nu pot rula dect a a a n a a cadrul unui proces. O alt deosebire rezult din faptul c ecare proces n a a a are propria sa memorie (propriul su spatiu de adrese) iar la crearea unui nou a proces (fork) este realizat o copie exact a procesului printe: cod i date, a a a s timp ce la crearea unui r nu este copiat dect codul procesului printe, n a a toate rele de executie avnd acces la aceleai date, datele procesului original. a s Aadar, un r mai poate privit i ca un context de executie cadrul unui s s n proces. Firele de executie sunt utile multe privinte, a uzual ele sunt folosite n ns pentru executarea unor operatii consumatoare de timp fr a bloca procesul aa principal: calcule matematice, ateptarea eliberrii unei resurse, desenarea s a componentelor unei aplicatii GUI, etc. De multe ori ori, rele si desfoar as a activitatea fundal a, evident, acest lucru nu este obligatoriu. n ns

12.2

Crearea unui r de executie

Ca orice alt obiect Java, un r de executie este o instant a unei clase. Firele a de executie denite de o clas vor avea acelai cod i, prin urmare, aceeai a s s s

12.2. CREAREA UNUI FIR DE EXECUTIE

345

secventa de instructiuni. Crearea unei clase care s deneasc re de executie a a poate facut prin dou modaliti: a a at prin extinderea clasei Thread. prin implementarea interfetei Runnable. Orice clas ale crei instante vor executate separat a a ntr-un r propriu trebuie declarat ca ind de tip Runnable. Aceasta este o interfat care a a contine o singur metod i anume metoda run. Aadar, orice clas ce descrie a as s a re de executie va contine metoda run care este implementat codul ce va n rulat. Interfata Runnable este conceput ca ind un protocol comun pentru a obiectele care doresc s execute un anumit cod pe durata existentei lor. a Cea mai important clas care implementeaz interfata Runnable este a a a Thread. Aceasta implementeaz un r de executie generic care, implicit, nu a face nimic; cu alte cuvinte, metoda run nu contine nici un cod. Orice r de executie este o instant a clasei Thread sau a unei subclase a sa. a

12.2.1

Extinderea clasei Thread

Cea mai simpl metod de a crea un r de executie care s realizeze o anumit a a a a actiune este prin extinderea clasei Thread i supradenirea metodei run a s acesteia. Formatul general al unei astfel de clase este: public class FirExcecutie extends Thread { public FirExcecutie(String nume) { // Apelam constructorul superclasei super(nume); } public void run() { // Codul firului de executie ... } } Prima metod a clasei este constructorul, care primete ca argument un a s ir ce va reprezenta numele rului de executie. In cazul care nu vrem s s n a dm nume relor pe care le crem, atunci putem renunta la supradenirea a a

346

CAPITOLUL 12. FIRE DE EXECUTIE

acestui constructor i s folosim constructorul implicit, fr argumente, care s a aa creeaz un r de executie fr nici un nume. Ulterior, acesta poate primi un a aa nume cu metoda setName. Evident, se pot deni i alti constructori, acetia s s inde utili atunci cnd vrem s trimitem diveri parametri de initializare a a s rului nostru. A dou metod este metoda run, inima oricrui r de a a a executie, care scriem efectiv codul care trebuie s se execute. n a Un r de executie creat nu este automat pornit, lansarea s ind realizeaz a a de metoda start, denit clasa Thread. a n // Cream firul de executie FirExecutie fir = new FirExecutie("simplu"); // Lansam in executie fir.start(); S considerm continuare un exemplu care denim un r de executie a a n n ce aeaz numerele s a ntregi dintr-un interval, cu un anumit pas. Listing 12.1: Folosirea clasei Thread
class AfisareNumere extends Thread { private int a , b , pas ; public AfisareNumere ( int a , int b , int pas ) { this . a = a ; this . b = b ; this . pas = pas ; } public void run () { for ( int i = a ; i <= b ; i += pas ) System . out . print ( i + " " ) ; } } public class TestThread { public static void main ( String args []) { AfisareNumere fir1 , fir2 ; fir1 = new AfisareNumere (0 , 100 , 5) ; // Numara de la 0 la 100 cu pasul 5 fir2 = new AfisareNumere (100 , 200 , 10) ;

12.2. CREAREA UNUI FIR DE EXECUTIE


// Numara de la 100 la 200 cu pasul 10 fir1 . start () ; fir2 . start () ; // Pornim firele de executie // Ele vor fi distruse automat la terminarea lor } }

347

Gndind secvential, s-ar crede c acest program va aa prima dat nua a s a merele de la 0 la 100 cu pasul 5, apoi numerele de la 100 la 200 cu pasul 10, ntruct primul apel este ctre contorul fir1, deci rezultatul aat pe ecran a a s ar trbui s e: a 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 100 110 120 130 140 150 160 170 180 190 200 In realitate a, rezultatul obtinut va o intercalare de valori produse de ns cele dou re ce ruleaz simultan. La rulri diferite se pot obtine rezultate a a a diferite deoarece timpul alocat ecrui r de executie poate s nu e acelai, a a s el ind controlat de procesor ntr-o manier aparent aleatoare. Un posibil a rezultat al programului de mai sus: 0 100 5 110 10 120 15 130 20 140 25 150 160 170 180 190 200 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100

12.2.2

Implementarea interfetei Runnable

Ce facem a cnd dorim s crem o clas care instantiaz re de executie ns a a a a a dar aceasta are deja o superclas, tiind c Java nu este permis motenirea a s a n a s multipl ? a class FirExecutie extends Parinte, Thread // incorect ! In acest caz, nu mai putem extinde clasa Thread ci trebuie s implementm a a direct interfata Runnable. Clasa Thread implementeaz ea ai interfata a nss Runnable i, din acest motiv, la extinderea ei obtineam implementarea indis rect a interfetei. Aadar, interfat Runnable permite unei clase s e activ, a s a a a fr a extinde clasa Thread. aa Interfata Runnable se gsete pachetul java.lang i este denit astfel: a s n s a

348

CAPITOLUL 12. FIRE DE EXECUTIE

public interface Runnable { public abstract void run(); } Prin urmare, o clas care instantiaz re de executie prin implementarea a a interfetei Runnable trebuie obligatoriu s implementeze metoda run. O astfel a de clas se mai numete clas activ i are urmtoarea structur: a s a as a a public class ClasaActiva implements Runnable { public void run() { //Codul firului de executie ... } } Spre deosebire de modalitatea anterioar, se pierde a tot suportul oferit a ns de clasa Thread. Simpla instantiere a unei clase care implemeneaz interfata a Runnable nu creeaz nici un r de executie, crearea acestora trebuind fcut a a a explicit. Pentru a realiza acest lucru trebuie s instantiem un obiect de tip a Thread ce va reprezenta rul de executie propriu zis al crui cod se gasete a s clasa noastr. Acest lucru se realizeaz, ca pentru orice alt obiect, prin n a a instructiunea new, urmat de un apel la un constructor al clasei Thread, a a ns nu la oricare dintre acetia. Trebuie apelat constructorul care s primeasc s a a drept argument o instant a clasei noastre. Dup creare, rul de executie a a poate lansat printr-un apel al metodei start. ClasaActiva obiectActiv = new ClasaActiva(); Thread fir = new Thread(obiectActiv); fir.start(); Aceste operatiuni pot fcute chiar cadrul clasei noastre: a n public class FirExecutie implements Runnable { private Thread fir = null; public FirExecutie() if (fir == null) { fir = new Thread(this);

12.2. CREAREA UNUI FIR DE EXECUTIE fir.start(); } } public void run() { //Codul firului de executie ... } }

349

Specicarea argumentului this constructorul clasei Thread determin n a crearea unui r de executie care, la lansarea sa, va apela metoda run din clasa curent. Aadar, acest constructor accept ca argument orice instant a s a a a unei clase Runnable. Pentru clasa FirExecutie dat mai sus, lansarea a rului va fcut automat la instantierea unui obiect al clasei: a a FirExecutie fir = new FirExecutie();

Atentie Metoda run nu trebuie apelat explicit, acest lucru realizndu-se automat a a la apelul metodei start. Apelul explicit al metodei run nu va furniza nici o eroare, a aceasta va executat ca orice alt metoda, i nu separat ns a a s ntr-un r.

S considerm urmtorul a a a folosind interfata Runnable. anumit tip, pe o suprafat de a re de executie care vor rula suprafata sa.

exemplu care crem dou re de executie n a a Fiecare r va desena guri geometrice de un desenare de tip Canvas. Vom porni apoi dou a concurent, desennd guri diferite, ecare pe a

Listing 12.2: Folosirea interfetei Runnable


import java . awt .*; import java . awt . event .*; class Plansa extends Canvas implements Runnable { // Deoarece Plansa extinde Canvas ,

350

CAPITOLUL 12. FIRE DE EXECUTIE

// nu mai putem extinde clasa Thread Dimension dim = new Dimension (300 , 300) ; Color culoare ; String figura ; int x =0 , y =0 , r =0; public Plansa ( String figura , Color culoare ) { this . figura = figura ; this . culoare = culoare ; } public Dimension getPreferredSize () { return dim ; } public void paint ( Graphics g ) { // Desenam un chenar g . setColor ( Color . black ) ; g . drawRect (0 , 0 , dim . width -1 , dim . height -1) ; // Desenam figura la coordonatele calculate // de firul de executie g . setColor ( culoare ) ; if ( figura . equals ( " patrat " ) ) g . drawRect (x , y , r , r ) ; else if ( figura . equals ( " cerc " ) ) g . drawOval (x , y , r , r ) ; } public void update ( Graphics g ) { paint ( g ) ; // Supradefinim update ca sa nu mai // fie stearsa suprafata de desenare } public void run () { /* Codul firului de executie : Afisarea a 100 de figuri geometrice la pozitii si dimensiuni calculate aleator . Intre doua afisari , facem o pauza de 50 ms */ for ( int i =0; i <100; i ++) {

12.2. CREAREA UNUI FIR DE EXECUTIE


x = ( int ) ( Math . random () * dim . width ) ; y = ( int ) ( Math . random () * dim . height ) ; r = ( int ) ( Math . random () * 100) ; try { Thread . sleep (50) ; } catch ( InterruptedEx c e p t i o n e ) {} repaint () ; } } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; // Cream doua obiecte active de tip Plansa Plansa p1 = new Plansa ( " patrat " , Color . blue ) ; Plansa p2 = new Plansa ( " cerc " , Color . red ) ; // Acestea extind Canvas , le plasam pe fereastra setLayout ( new GridLayout (1 , 2) ) ; add ( p1 ) ; add ( p2 ) ; pack () ; // Pornim doua fire de executie , care vor // actualiza desenul celor doua planse new Thread ( p1 ) . start () ; new Thread ( p2 ) . start () ; } } public class TestRunnable { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test Runnable " ) ; f . show () ; } }

351

352

CAPITOLUL 12. FIRE DE EXECUTIE

12.3

Ciclul de viat al unui r de executie a

Fiecare r de executie are propriul su ciclu de viat: este creat, devine activ a a prin lansarea sa i, la un moment dat, se termin. In continuare, vom analiza s a mai ndeaproape strile care se poate gsi un r de executie. Diagrama a n a de mai jos ilustreaz generic aceste stri precum i metodele care provoaca a a s tranzitia dintr-o stare alta: n

Aadar, un r de executie se poate gsi una din urmtoarele patru s a n a stri: a New Thread Runnable Not Runnable Dead

Starea New Thread Un r de executie se gsete aceast stare imediat dup crearea sa, cu alte a s n a a cuvinte dup instantierea unui obiect din clasa Thread sau dintr-o subclas a a a sa. Thread fir = new Thread(obiectActiv); // fir se gaseste in starea "New Thread"

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE

353

In aceast stare rul este vid, el nu are alocate nici un fel de resurse sisa tem i singura operatiune pe care o putem executa asupra lui este lansarea s executie, prin metoda start. Apelul oricrei alte metode afar de start n a n a nu are nici un sens i va provoca o exceptie de tipul IllegalThreadStateException. s

Starea Runnable Dup apelul metodei start un r va trece starea Runnable, adic va a n a executie. n fir.start(); //fir se gaseste in starea "Runnable" Metoda start realizez urmtoarele operatiuni necesare rulrii rului de a a a executie: Aloc resursele sistem necesare. a Planic rul de executie la procesor pentru a lansat. a Apeleaz metoda run a obiectului activ al rului. a Un r aat starea Runnable nu n nseamn neaprat c se geste a a a as efectiv executie, adic instructiunile sale sunt interpretate de procesor. n a Acest lucru se ampl din cauza c majoritatea calculatoarelor au un singur nt a a procesor iar acesta nu poate rula simultan toate rele de executie care se gasesc starea Runnable. Pentru a rezolva aceasta problem exist o n a a planicare care s partajeze dinamic i corect procesorul a s ntre toate rele de executie care sunt starea Runnable. Aadar, un r care ruleaz poate n s a s-i atepte de fapt rndul la procesor. as s a

Starea Not Runnable Un r de executie poate ajunge aceaat stare una din urmtoarele n a n a situatii: Este adormit prin apelul metodei sleep; A apelat metoda wait, ateptnd ca o anumit conditie s e satisfas a a a cut; a

354

CAPITOLUL 12. FIRE DE EXECUTIE

Este blocat ntr-o operatie de intrare/ieire. s Metoda sleep este o metod static a clasei Thread care provoac o a a a pauz timpul rulrii rului curent aat executie, cu alte cuvinte a n a n l adoarme pentru un timp specicat. Lungimea acestei pauze este specicat a milisecunde i chiar nanosecunde. Intruct poate provoca exceptii de tipul n s a InterruptedException, apelul acestei metode se face ntr-un bloc de tip try-cacth: try { // Facem pauza de o secunda Thread.sleep(1000); } catch (InterruptedException e) { ... } Observati c metoda ind static apelul ei nu se face pentru o instant anume a a a a clasei Thread. Acest lucru este foarte normal deoarece, la un moment dat, un singur r este executie i doar pentru acesta are sens adormirea sa. n s In intervalul care un r de executie doarme, acesta nu va execut n chiar dac procesorul devine disponibil. Dup expirarea intervalului specia a cat rul revine starea Runnable iar dac procesorul este continuare n a n disponibil va continua executia. si Pentru ecare tip de intrare starea Not Runnable, exist o secvent n a a specic de ieire din starea repectiv, care readuce rul de executie starea a s a n Runnable. Acestea sunt: Dac un r de executie a fost adormit, atunci el devine Runnable a doar dup scurgerea intervalului de timp specicat de instructiunea a sleep. Dac un r de executie ateapt o anumit conditie, atunci un alt a s a a obiect trebuie s informeze dac acea conditie este a l a ndeplinit sau a nu; acest lucru se realizeaz prin instructiunile notify sau notifyAll a (vezi Sincronizarea relor de executie). Dac un r de executie este blocat a ntr-o operatiune de intrare/ieire s atunci el redevine Runnable atunci cnd acea operatiune s-a termia nat.

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE

355

Starea Dead Este starea care ajunge un r de executie la terminarea sa. Un r nu n poate oprit din program printr-o anumit metod, ci trebuie s se termine a a a mod natural la n ncheierea metodei run pe care o execut. Spre deosebire de a versiunile curente ale limbajului Java, versiunile mai vechi exista metoda n stop a clasei Thread care termina fortat un r de executie, a aceasta a ns fost eliminat din motive de securitate. Aadar, un r de executie trebuie a s s-i aranjeze singur propria sa moarte. as

12.3.1

Terminarea unui r de executie

Dup cum am vazut, un r de executie nu poate terminat fortat de ctre a a program ci trebuie s-i aranjeze singur terminarea sa. Acest lucru poate as realizat dou modaliti: n a at 1. Prin scrierea unor metode run care s-i termine executia mod natas n ural. La terminarea metodei run se va termina automat i rul de s executie, acesta intrnd starea Dead. Ambele exemple anteriorare a n se ncadreaz aceast categorie. a n a // Primul exemplu public void run() { for(int i = a; i <= b; i += pas) System.out.print(i + " " ); } Dup aarea numerelor din intervalul specicat, metoda se termin a s a i, odat cu ea, se va termina i rul de executie repsectiv. s a s 2. Prin folosirea unei variabile de terminare. In cazul cnd metoda run a trebuie s execute o bucl innit atunci aceasta trebuie controlat a a a a printr-o variabil care s opreasc ciclul atunci cnd dorim ca rul de a a a a executie s se termine. Uzual, vom folosi o variabil membr a clasei a a a care descrie rul de executie care e este public, e este asociat cu o a a metod public care permite schimbarea valorii sale. a a S considerm exemplul unui r de executie care trebuie s numere sea a a cundele scurse pn la apsarea tastei Enter. a a a

356

CAPITOLUL 12. FIRE DE EXECUTIE Listing 12.3: Folosirea unei variabile de terminare

import java . io .*; class NumaraSecunde extends Thread { public int sec = 0; // Folosim o variabila de terminare public boolean executie = true ; public void run () { while ( executie ) { try { Thread . sleep (1000) ; sec ++; System . out . print ( " . " ) ; } catch ( Interr u pt ed Ex ce pt io n e ) {} } } } public class TestTerminare { public static void main ( String args []) throws IOException { NumaraSecunde fir = new NumaraSecunde () ; fir . start () ; System . out . println ( " Apasati tasta Enter " ) ; System . in . read () ; // Oprim firul de executie fir . executie = false ; System . out . println ( "S - au scurs " + fir . sec + " secunde " ) ; } }

Nu este necesar distrugerea explicit a unui r de executie. Sistemul a a Java de colectare a gunoiului se ocup de acest lucru. Setarea valorii a null pentru variabila care referea instanta rului de executie va uura a s ns activitatea procesului gc. Metoda System.exit va oprit fortat toate rele de executie i va termina s aplicatia curent. a

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE

357

Pentru a testa dac un r de executie a fost pornit dar nu s-a terminat a a putem folosi metoda isAlive. Metoda returneaz: nc a true - dac rul este una din strile Runnable sau Not Runnable a n a false - dac rul este una din starile New Thread sau Dead a n Intre strile Runnable sau Not Runnable, repectiv New Thread a sau Dead nu se poate face nici o diferentiere. NumaraSecunde fir = new NumaraSecunde(); // isAlive retuneaza false (starea este New Thread) fir.start(); // isAlive retuneaza true (starea este Runnable) fir.executie = false; // isAlive retuneaza false (starea este Dead)

12.3.2

Fire de executie de tip daemon

Un proces este considerat executie dac contine cel putin un r de executie n a activ. Cu alte cuvinte, la rularea unei aplicatii, maina virtual Java nu se va s a opri dect atunci cnd nu mai exist nici un r de executie activ. De multe ori a a a a dorim s folosim re care s realizeze diverse activiti, eventual periodic, ns a a at pe toat durata de executie a programului iar momentul terminrii acestuia a n a s se termine automat i rele respective. Aceste re de executie se numesc a s demoni. Dup crearea sa, un r de executie poate fcut demon, sau scos din a a aceast stare, cu metoda setDaemon. a Listing 12.4: Crearea unui r de excutie de tip daemon
class Beeper implements Runnable { public void run () { while ( true ) { java . awt . Toolkit . getDef aultToo lkit () . beep () ; try { Thread . sleep (1000) ; } catch ( InterruptedEx c e p t i o n e ) {} }

358
} }

CAPITOLUL 12. FIRE DE EXECUTIE

public class TestDaemon { public static void main ( String args []) throws java . io . IOException { Thread t = new Thread ( new Beeper () ) ; t . setDaemon ( true ) ; t . start () ; System . out . println ( " Apasati Enter ... " ) ; System . in . read () ; // " Demonul " se termina automat // la terminarea aplicatiei } }

12.3.3

Stabilirea prioritilor de executie at

Majoritatea calculatoarelor au un sigur procesor, ceea ce nseamn c rele a a de executie trebuie s-i a s mpart accesul la acel procesor. Executia a ntr-o anumit ordine a mai multor re de executie pe un numr limitat de procesoare a a se numete planicare (scheduling). Sistemul Java de executie a programelor s implementeaz un algoritm simplu, determinist de planicare, cunoscut sub a numele de planicare cu prioriti xate. at Fiecare r de executie Java primete la crearea sa o anumit priori s a tate. O prioritate este de fapt un numr a ntreg cu valori cuprinse ntre s MIN PRIORITY i MAX PRIORITY. Implicit, prioritatea unui r nou creat are valoarea NORM PRIORITY. Aceste trei constante sunt denite clasa Thread n astfel: public static final int MAX_PRIORITY = 10; public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY= 5; Schimbarea ulterioar a prioritii unui r de executie se realizeaz cu metoda a at a setPriority a clasei Thread. La nivelul sistemului de operare, exist dou modele de lucru cu re de a a executie:

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE

359

Modelul cooperativ, care rele de executie decid cnd s cedeze pron a a cesorul; dezavantajul acestui model este c unele re pot acapara proa cesorul, nepermitnd i executia altora pn la terminarea lor. a s a a Modelul preemptiv, care rele de executie pot n ntrerupte oricnd, a dup ce au fost lsate s ruleze o perioad, urmnd s e reluate dup a a a a a a a ce i celelalte re aate executie au avut acces la procesor; acest s n sistem se mai numete cu cuante de timp, dezavantajul su ind s a nevoia de a sincroniza accesul relor la resursele comune. Aadar, modelul cooperativ rele de executie sunt responsabile cu pars m tajarea timpului de executie, timp ce modelul preemptiv ele trebuie n n s partajeze resursele comune. Deoarece specicatiile mainii virtuale Java a s nu impun folosirea unui anumit model, programele Java trebuie scrise astfel at s functioneze corect pe ambele modele. In continuare, vom mai detalia nc a putin aceste aspecte. Planicatorul Java lucreaz modul urmator: dac la un moment dat a n a sunt mai multe re de executie starea Runnable, adic sunt pregatite n a pentru a rulate, planicatorul va alege pe cel cu prioritatea cea mai l mare pentru a-l executa. Doar cnd rul de executie cu prioritate maxim se a a termin, sau este suspendat din diverse motive, va ales un r cu o prioritate a mai mic. In cazul care toate rele au aceeai prioritate ele sunt alese pe a n s rnd, dup un algoritm simplu de tip round-robin. De asemenea, dac a a a un r cu prioritate mai mare dect rul care se execut la un moment dat a a solicit procesorul, atunci rul cu prioritate mai mare este imediat trecut a n executie iar celalalt trecut asteptare. Planicatorul Java nu va n ntrerupe a un r de executie favoarea altuia de aceeasi prioritate, a acest lucru ns n ns poate face sistemul de operare cazul care acesta aloc procesorul l n n a n cuante de timp (un astfel de SO este Windows). Aadar, un r de executie Java cedeaz procesorul una din situatiile: s a n un r de executie cu o prioritate mai mare solicit procesorul; a metoda sa run se termin; a face explicit acest lucru apelnd metoda yield; a timpul alocat pentru executia s a expirat (pe SO cu cuante de timp). a

360

CAPITOLUL 12. FIRE DE EXECUTIE

Atentie In nici un caz corectitudinea unui program nu trebuie s se bazeze pe a mecansimul de planicare a relor de executie, deoarece acesta poate diferit de la un sistem de operare la altul.

Un r de executie de lung durat i care nu cedeaz explicit procesorul la a as a anumite intervale de timp astfel at s poata executate i celelalte re de nc a s executie se numete r de executie egoist. Evident, trebuie evitat scrierea lor s a ntruct acapareaz pe termen nedenit procesorul, blocnd efectiv executia a a a celorlalte re de executie pn la terminarea sa. Unele sistemele de oper a a are combat acest tip de comportament prin metoda alocrii procesorului a n cuante de timp ecrui r de executie, a nu trebuie s ne bazm pe acest a ns a a lucru la scrierea unui program. Un r de executie trebuie s e corect a fatde celelalte re i s cedeze periodic procesorul astfel at toate s aib a s a nc a a posibilitatea de a se executa. Listing 12.5: Exemplu de r de executie egoist
class FirEgoist extends Thread { public FirEgoist ( String name ) { super ( name ) ; } public void run () { int i = 0; while ( i < 100000) { // Bucla care acapareaza procesorul i ++; if ( i % 100 == 0) System . out . println ( getName () + " a ajuns la " + i ) ; // yield () ; } } } public class TestFirEgoist { public static void main ( String args []) { FirEgoist s1 , s2 ; s1 = new FirEgoist ( " Firul 1 " ) ; s1 . setPriority ( Thread . MAX_PRIORITY ) ; s2 = new FirEgoist ( " Firul 2 " ) ; s2 . setPriority ( Thread . MAX_PRIORITY ) ;

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE


s1 . start () ; s2 . start () ; } }

361

Firul de executie s1 are prioritate maxim i pn nu-i va termina a s a a s executia nu-i va permite rului s2 s execute nici o instructiune, acaparnd a a efectiv procesorul. Rezultatul va arta astfel: a Firul Firul Firul ... Firul Firul Firul Firul ... Firul Firul 1 a ajuns la 100 1 a ajuns la 200 1 a ajuns la 300 1 1 2 2 a a a a ajuns ajuns ajuns ajuns la la la la 99900 100000 100 200

2 a ajuns la 99900 2 a ajuns la 100000

Rezolvarea acestei probleme se face e prin intermediul metodei statice yield a clasei Thread, care determin rul de executie curent s se opreasc tema a a porar, dnd ocazia i altor re s se execute, e prin adormirea temporar a s a a a rului curent cu ajutorul metodei sleep. Prin metoda yield un r de executie nu cedeaz procesorul dect relor de executie care au aceeai pri a a s oritate cu a sa i nu celor cu prioriti mai mici. Decomentnd linia care s at a n apelm yeld din exemplul anterior, executia celor dou re se va intercala. a a ... Firul Firul Firul Firul Firul Firul ...

1 1 2 1 2 2

a a a a a a

ajuns ajuns ajuns ajuns ajuns ajuns

la la la la la la

31900 32000 100 32100 200 300

362

CAPITOLUL 12. FIRE DE EXECUTIE

12.3.4

Sincronizarea relor de executie

Pn acum am vzut cum putem crea re de executie independente i asa a a s incrone, cu alte cuvinte care nu depind nici un fel de executia sau de n rezultatele altor re. Exist a numeroase situatii cnd re de executie a ns a separate, dar care ruleaz concurent, trebuie s comunice a a ntre ele pentru a accesa diferite resurse comune sau pentru a-i transmite dinamic rezuls tatele muncii lor. Cel mai elocvent scenariu care rele de executie n trebuie s se comunice a ntre ele este cunoscut sub numele de problema productorului/consumatorului, care productorul genereaz un ux de date a n a a care este preluat i prelucrat de ctre consumator. s a S considerm de exemplu o aplicatie Java care un r de executie (proa a n ductorul) scrie date a ntr-un ier timp ce alt r de executie (consumas n torul) citete date din acelai ier pentru a le prelucra. Sau, s presupunem s s s a c productorul genereaz nite numere i le plaseaz, pe rnd, a a a s s a a ntr-un buer iar consumatorul citete numerele din acel buer pentru a le procesa. In ams bele cazuri avem de-a face cu re de executie concurente care folosesc o resurs comun: un ier, respectiv o zon de memorie i, din acest motiv, a a s a s ele trebuie sincronizate ntr-o manier care s permit decurgerea normal a a a a a activitii lor. at

12.3.5

Scenariul productor / consumator a

Pentru a ntelege mai bine modalitatea de sincronizare a dou re de executie a s implementm efectiv o problem de tip productor/consumator. S cona a a a a siderm urmtoarea situatie: a a Productorul genereaz numerele a a ntregi de la 1 la 10, ecare la un interval neregulat cuprins ntre 0 i 100 de milisecunde. Pe msura ce s a le genereaz a ncearc s le plaseze a a ntr-o zon de memorie (o variabil a a ntreaga) de unde s e citite de ctre consumator. a a Consumatorul va prelua, pe rnd, numerele generate de ctre proa a ductor i va aa valoarea lor pe ecran. a s s Pentru a accesibil ambelor re de executie, vom a ncapsula variabila ce va contine numerele generate ntr-un obiect descris de clasa Buffer i care va s avea dou metode put (pentru punerea unui numar buer) i get (pentru a n s obtinerea numarului din buer).

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE

363

Fr a folosi nici un mecanism de sincronizare clasa Buffer arat astfel: aa a Listing 12.6: Clasa Buffer fr sincronizare aa
class Buffer { private int number = -1; public int get () { return number ; } public void put ( int number ) { this . number = number ; } }

Vom implementa acum clasele Producator i Consumator care vor descrie s cele dou re de executie. Ambele vor avea o referinta comun la un obiect a a de tip Buffer prin intermediul cruia si comunic valorile. a a Listing 12.7: Clasele Producator i Consumator s
class Producator extends Thread { private Buffer buffer ; public Producator ( Buffer b ) { buffer = b ; } public void run () { for ( int i = 0; i < 10; i ++) { buffer . put ( i ) ; System . out . println ( " Producatorul a pus :\ t " + i ) ; try { sleep (( int ) ( Math . random () * 100) ) ; } catch ( InterruptedE x c e p t i o n e ) { } } } } class Consumator extends Thread { private Buffer buffer ; public Consumator ( Buffer b ) { buffer = b ;

364
}

CAPITOLUL 12. FIRE DE EXECUTIE

public void run () { int value = 0; for ( int i = 0; i < 10; i ++) { value = buffer . get () ; System . out . println ( " Consumatorul a primit :\ t " + value ) ; } } } public class TestSincron izare1 { public static void main ( String [] args ) { Buffer b = new Buffer () ; Producator p1 = new Producator ( b ) ; Consumator c1 = new Consumator ( b ) ; p1 . start () ; c1 . start () ; } }

Dup cum ne ateptam, rezultatul rulrii acestui program nu va rezolva a s a nici pe departe problema propus de noi, motivul ind lipsa oricrei sina a cronizri a ntre cele dou re de executie. Mai precis, rezultatul va ceva de a forma: Consumatorul Consumatorul Producatorul Consumatorul Consumatorul Consumatorul Consumatorul Consumatorul Consumatorul Consumatorul Consumatorul Producatorul Producatorul Producatorul Producatorul Producatorul a a a a a a a a a a a a a a a a primit: primit: pus: primit: primit: primit: primit: primit: primit: primit: primit: pus: pus: pus: pus: pus: -1 -1 0 0 0 0 0 0 0 0 0 1 2 3 4 5

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE Producatorul Producatorul Producatorul Producatorul a a a a pus: pus: pus: pus: 6 7 8 9

365

Ambele re de executie acceseaz resursa comun, adic obiectul de tip a a a Buffer, ntr-o manier haotic i acest lucru se ampla din dou motive : a as nt Consumatorul nu ateapt s a nainte de a citi ca productorul s genereze a a un numr i va prelua de mai multe ori acelai numr. a s s a Productorul nu ateapt consumatorul s preia numrul generat a s a a a nainte de a produce un altul, felul acesta consumatorul va rata cu sigurant n a unele numere ( cazul nostru aproape pe toate). n Problema care se ridic acest moment este: cine trebuie s se ocupe de a n a sincronizarea celor dou re de executie : clasele Producator i Consumator a s sau resursa comuna Buffer ? Rspunsul este evident: resursa comun a a Buffer, deoarece ea trebuie s permita sau nu accesul la continutul su i a a s nu rele de executie care o folosesc. In felul acesta efortul sincronizrii este a transferat de la productor/consumator la un nivel mai jos, cel al resursei a critice. Activitile productorului i consumatorului trebuie sincronizate la nivelul at a s resursei comune dou privinte: n a Cele dou re de executie nu trebuie s acceseze simultan buer-ul; a a acest lucru se realizeaz prin blocarea obiectului Buffer atunci cnd a a este accesat de un r de executie, astfel at nici nu alt r de executie nc s nu-l mai poat accesa (vezi Monitoare). a a Cele dou re de executie trebuie s se coordoneze, adic productorul a a a a trebuie s gseasc o modalitate de a spune consumatorului c a a a a a plasat o valoare buer, iar consumatorul trebuie s comunice pron a ductorului c a preluat aceast valoare, pentru ca acesta s poat gena a a a a era o alta. Pentru a realiza aceasta comunicare, clasa Thread pune la dispozitie metodele wait, notify, notifyAll. (vezi Semafoare). Folosind sincronizarea clasa Buffer va arta astfel: a

366

CAPITOLUL 12. FIRE DE EXECUTIE Listing 12.8: Clasa Buffer cu sincronizare

class Buffer { private int number = -1; private boolean available = false ; public synchronized int get () { while (! available ) { try { wait () ; // Asteapta producatorul sa puna o valoare } catch ( Inter r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } available = false ; notifyAll () ; return number ; } public synchronized void put ( int number ) { while ( available ) { try { wait () ; // Asteapta consumatorul sa preia valoarea } catch ( Inter r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } this . number = number ; available = true ; notifyAll () ; } }

Rezultatul obtinut va cel scontat: Producatorul Consumatorul Producatorul Consumatorul ... Producatorul Consumatorul a a a a pus: primit: pus: primit: 0 0 1 1

a pus: 9 a primit: 9

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE

367

12.3.6

Monitoare

Denitie Un segment de cod ce gestioneaz o resurs comun mai multor de re de a a a executie separate concurente se numete sectiune critic. In Java, o sectiune s a critic poate un bloc de instructiuni sau o metod. a a Controlul accesului ntr-o sectiune critic se face prin cuvntul cheie syn a a chronized. Platforma Java asociaz un monitor (lact) ecrui obiect al a a a unui program aat executie. Acest monitor va indica dac resursa critic n a a este accesat de vreun r de executie sau este liber, cu alte cuvinte mona a itorizeaz resursa respectiv. In cazul care este accesat, va pune un a a n a lact pe aceasta, astfel at s a nc a mpiedice accesul altor re de executie la ea. In momentul cnd resursa este eliberat lactul va eliminat, pentru a a a a permite accesul altor re de executie. In exemplul de tip productor/consumator de mai sus, sectiunile critice a sunt metodele put i get iar resursa critic comun este obiectul buffer. s a a Consumatorul nu trebuie s acceseze buer-ul cnd producatorul tocmai a a pune o valoare el, iar productorul nu trebuie s modice valoarea din n a a buer momentul cnd aceasta este citit de ctre consumator. n a a a public synchronized int get() { ... } public synchronized void put(int number) { ... } S observam c ambele metode au fost declarate cu modicatorul synchronized. a a Cu toate acestea, sistemul asociaz un monitor unei instante a clasei Buffer a i nu unei metode anume. In momentul care este apelat o metod sins n a a cronizat, rul de executie care a facut apelul va bloca obiectul a crei metod a a a o acceseaz, ceea ce a nseamn c celelalte re de executie nu vor mai putea a a accesa resursele critice ale acelui obiect. Acesta este un lucru logic, deoarece mai multe sectiuni critice ale unui obiect gestioneaz de fapt o singur resurs a a a critic. a In exemplul nostru, atunci cnd producatorul apeleaz metoda put pena a tru a scrie un numr, va bloca tot obiectul buffer, astfel c rul de executie a a

368

CAPITOLUL 12. FIRE DE EXECUTIE

consumator nu va avea acces la metoda get, i reciproc. s public synchronized void put(int number) { // buffer blocat de producator ... // buffer deblocat de producator } public synchronized int get() { // buffer blocat de consumator ... // buffer deblocat de consumator }

Monitoare ne Adeseori, folosirea unui monitor pentru ntreg obiectul poate prea restrictiv. De ce s blocm toate resursele unui obiect dac un r de executie a a a a nu dorete dect accesarea uneia sau a ctorva dintre ele ? Deoarece orice s a a obiect are un monitor, putem folosi obiecte ctive ca lacte pentru ecare a din resursele obiectului nostru, ca exemplul de mai jos: n class MonitoareFine { //Cele doua resurse ale obiectului Resursa x, y; //Folosim monitoarele a doua obiecte fictive Object xLacat = new Object(), yLacat = new Object(); public void metoda() { synchronized(xLacat) { // Accesam resursa x } // Cod care nu foloseste resursele comune ... synchronized(yLacat) { // Accesam resursa y }

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE ... synchronized(xLacat) { synchronized(yLacat) { // Accesam x si y } } ... synchronized(this) { // Accesam x si y } } }

369

Metoda de mai sus nu a fost declarat cu synchronized ceea ce ar dea terminat blocarea tuturor resurselor comune la accesarea obiectului respectiv de un r de executie, ci au fost folosite monitoarele unor obiecte ctive pentru a controla folosirea ecrei resurs parte. a a n

12.3.7

Semafoare

Obiectul de tip Buffer din exemplul anterior are o variabil membr privat a a a numit number, care este memorat numrul pe care comunic produa n a l a catorul i pe care preia consumatorul. De asemenea, mai are o variabil s l a privat logic available care ne d starea buer-ului: dac are valoarea true a a a a nseamn c productorul a pus o valoare buer i consumatorul nu a a a a n s preluat-o nca; dac este false, consumatorul a preluat valoarea din buer a dar productorul nu a pus deocamdat alta la loc. Deci, la prima vedere, a a metodele clasei Buffer ar trebui s arate astfel: a public synchronized int get() { while (!available) { // Nimic - asteptam ca variabila sa devina true } available = false; return number; } public synchronized int put(int number) { while (available) {

370

CAPITOLUL 12. FIRE DE EXECUTIE // Nimic - asteptam ca variabila sa devina false } available = true; this.number = number;

} Varianta de mai sus, dei pare corect, nu este. Aceasta deoarece ims a plementarea metodelor este selsh, cele dou metode asteapt mod a si a n egoist conditia de terminare. Ca urmare, corectitudinea functionrii va de a pinde de sistemul de operare pe care programul este rulat, ceea ce reprezint a o greeal de programare. s a Punerea corect a unui r de executie asteptare se realizeaz cu metoda a n a wait a clasei Thread, care are urmtoarele forme: a void wait( ) void wait( long timeout ) void wait( long timeout, long nanos ) Dup apelul metodei wait, rul de executie curent elibereaz monitorul a a asociat obiectului respectiv i ateapt ca una din urmtoarele conditii s e s s a a a ndeplinit: a Un alt r de executie informeaz pe cei care ateapt la un anumit a s a monitor s se trezeasc - acest lucru se realizeaz printr-un apel al a a a metodei notifyAll sau notify. Perioada de atepatare specicat a expirat. s a Metoda wait poate produce exceptii de tipul InterruptedException, atunci cnd rul de executie care ateapt (este deci starea Not Runnable) a s a n este ntrerupt din ateptare i trecut fortat starea Runnable, dei conditia s s n s ateptat nu era a s a nc ndeplinit. a Metoda notifyAll informeaz toate rele de executie care sunt asteptare a n la monitorul obiectului curent ndeplinirea conditiei pe care o ateptau. Metoda s notify informeaz doar un singur r de executie, specicat ca argument. a Reamintim varianta corect a clasei Buffer: a Listing 12.9: Folosirea semafoarelor
class Buffer { private int number = -1;

12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE


private boolean available = false ; public synchronized int get () { while (! available ) { try { wait () ; // Asteapta producatorul sa puna o valoare } catch ( InterruptedE x ce pt io n e ) { e . printStackTrace () ; } } available = false ; notifyAll () ; return number ; } public synchronized void put ( int number ) { while ( available ) { try { wait () ; // Asteapta consumatorul sa preia valoarea } catch ( InterruptedE x c e p t i o n e ) { e . printStackTrace () ; } } this . number = number ; available = true ; notifyAll () ; } }

371

12.3.8

Probleme legate de sincronizare

Din pcate, folosirea monitoarelor ridic i unele probleme. S analizm a a s a a cteva dintre ele i posibilele lor solutii: a s Deadlock Deadlock-ul este o problem clasic a a ntr-un mediu care ruleaz mai multe n a re de executie i const faptul c, la un moment dat, s a n a ntreg procesul se poate bloca deoarece unele re ateapt deblocarea unor monitoare care s a nu se vor debloca niciodat. Exist numeroase exemple acest sens, cea a a n mai cunoscut ind Problema lozolor. Reformulat, s ne imaginm a a a a dou persoane A i B (re de executie) care stau la aceeai mas i trea s s as

372

CAPITOLUL 12. FIRE DE EXECUTIE

buie s foloseasc comun cutitul i furculita (resursele comune) pentru a a a n s mnca. Evident, cele dou persoane doresc obtinerea ambelor resurse. S a a a presupunem c A a otinut cutitul i B furculita. Firul A se va bloca a s n ateptarea eliberrii furculitei iar rul A se va bloca atepatrea eliberrii s a n s a cutitului, ceea ce conduce la starea de deadlock. Dei acest exemplu este s desprins de realitate, exist numeroase situatii care fenomenul de deada n lock se poate manifesta, multe dintre acestea ind dicil de detectat. Exist cteva reguli ce pot aplicate pentru evitarea deadlock-ului: a a Firele de executie s solicite resursele aceeai ordine. Aceast abor a n s a dare elimin situatiile de ateptare circular. a s a Folosirea unor monitoare care s controleze accesul la un grup de resurse. a In cazul nostru, putem folosi un monitor tacmuri care trebuie blocat a nainte de a cere furculita sau cutitul. Folosirea unor variabile care s informeze disponibilitatea resurselor a fr a bloca monitoarele asociate acestora. aa Cel mai importat, conceperea unei arhitecturi a sistemului care s evite a pe ct posibil aparitia unor potentiale situatii de deaslock. a

Variabile volatile Cuvntul cheie volatile a fost introdus pentru a controla unele aspecte a legate de optimizrile efectuate de unele compilatoare. S considerm urmtorul a a a a exemplu: class TestVolatile { boolean test; public void metoda() { test = false; // * if (test) { // Aici se poate ajunge... } } }

12.4. GRUPAREA FIRELOR DE EXECUTIE

373

Un compilator care optimizeaz codul, poate decide c variabila test a a ind setat pe false, corpul if -ului nu se va executa i s exclud secventa a s a a respectiv din rezultatul compilrii. Dac aceast clas ar a accesat a a a a a ns a de mai multe re de executie, variabile test ar putea setat pe true de un a alt r, exact ntre instructiunile de atribuire i if ale rului curent. s Declararea unei variabile cu modicatorul volatile informeaz compilaa torul s nu optimizeze codul care aceasta apare, previzionnd valoarea pe a n a care variabila o are la un moment dat.

Fire de executie inaccesibile Uneori rele de executie sunt blocate din alte motive dect ateptarea la a s un monitor, cea mai frecvent situatie de acest tip ind operatiunile de ina trare/ieire (IO) blocante. Cnd acest lucru se ampl celelalte re de s a nt a executie trebuie s poat accesa continuare obiectul. Dar dac operatiunea a a n a IO a fost fcut a a ntr-o metod sincronizat, acest lucru nu mai este posibil, a a monitorul obiectului ind blocat de rul care ateapt de fapt s realizeze s a a operatia de intrare/ieire. Din acest motiv, operatiile IO nu trebuie fcute s a metode sincronizate. n

12.4

Gruparea relor de executie

Gruparea relor de executie pune la dispozitie un mecanism pentru manipu larea acestora ca un tot i nu individual. De exemplu, putem s pornim sau s s a a suspendm toate rele dintr-un grup cu un singur apel de metod. Gruparea a a relor de executie se realizeaz prin intermediul clasei ThreadGroup. a Fiecare r de executie Java este membru al unui grup, indiferent dac a specicm explicit sau nu acest lucru. Alierea unui r la un anumit grup a se realizeaz la crearea sa i devine permanent, sensul c nu vom putea a s a n a muta un r dintr-un grup altul, dup ce acesta a fost creat. In cazul n a n care crem un r folosind un constructor care nu specic din ce grup face a a parte, el va plasat automat acelai grup cu rul de executie care l-a n s creat. La pornirea unui program Java se creeaz automat un obiect de tip a ThreadGroup cu numele main, care va reprezenta grupul tuturor relor de executie create direct din program i care nu au fost ataate explicit altui s s grup. Cu alte cuvinte, putem s ignorm complet plasarea relor de executie a a grupuri i s lsm sistemul s se ocupe cu aceasta, adunndu-le pe toate n s a aa a a

374

CAPITOLUL 12. FIRE DE EXECUTIE

grupul main. n Exist situatii a cnd gruparea relor de executie poate uura substantial a ns a s manevrarea lor. Crearea unui r de executie i plasarea lui s ntr-un grup (altul dect cel implicit) se realizeaz prin urmtorii constructori ai clasei Thread: a a a public Thread(ThreadGroup group, Runnable target) public Thread(ThreadGroup group, String name) public Thread(ThreadGroup group, Runnable target, String name) Fiecare din aceti costructori creeaz un r de executie, initializeaz i s a l a s plaseaz l a ntr-un grup specicat ca argument. Pentru a aa crui grup a apartine un anumit r de executie putem folosi metoda getThreadGroup a clasei Thread. In exemplul urmtor vor create dou grupuri, primul cu a a dou re de executie iar al doilea cu trei: a ThreadGroup grup1 = new ThreadGroup("Producatori"); Thread p1 = new Thread(grup1, "Producator 1"); Thread p2 = new Thread(grup1, "Producator 2"); ThreadGroup Thread c1 = Thread c2 = Thread c3 = grup2 = new ThreadGroup("Consumatori"); new Thread(grup2, "Consumator 1"); new Thread(grup2, "Consumator 2"); new Thread(grup2, "Consumator 3");

Un grup poate avea ca printe un alt grup, ceea ce a nseamn c rele de a a executie pot plasate ntr-o ierarhie de grupuri, care rdcina este grupul n a a implicit main, ca gura de mai jos: n

12.4. GRUPAREA FIRELOR DE EXECUTIE S considerm un exemplu care listm rele de executie active: a a m a

375

Listing 12.10: Folosirea clasei ThreadGroup


public class TestThreadGroup { static class Dummy implements Runnable { public void run () { while ( true ) Thread . yield () ; } } public static void main ( String args []) { // Cream o fereastra pentru a fi create // automat firele de executie din AWT java . awt . Frame f = new java . awt . Frame ( " Test " ) ; // Cream un fir propriu new Thread ( new Dummy () , " Fir de test " ) . start () ; // Obtinem o referinta la grupul curent Thread firCurent = Thread . currentThread () ; ThreadGroup grupCurent = firCurent . getThreadGroup () ; // Aflam numarul firelor de executie active int n = grupCurent . activeCount () ; // Enumeram firele din grup Thread [] lista = new Thread [ n ]; grupCurent . enumerate ( lista ) ; // Le afisam for ( int i =0; i < n ; i ++) System . out . println ( " Thread # " + i + " = " + lista [ i ]. getName () ) ; } }

376

CAPITOLUL 12. FIRE DE EXECUTIE

12.5

Comunicarea prin uxuri de tip pipe

O modalitate deosebit de util prin care dou re de executie pot comunica a a este realizat prin intermediul canalelor de comunicatii (pipes). Acestea sunt a implementate prin uxuri descrise de clasele: PipedReader, PipedWriter - pentru caractere, respectiv PipedOutputStream, PipedInputStream - pentru octeti. Fluxurile pipe de ieire i cele de intrare pot conectate pentru a s s efectua transmiterea datelor. Acest lucru se realizeaz uzual prin intemediul a constructorilor: public PipedReader(PipedWriterpw) public PipedWriter(PipedReaderpr) In cazul care este folosit un constructor fr argumente, conectarea unui n aa ux de intrare cu un ux de ieire se face prin metoda connect: s public void connect(PipedWriterpw) public void connect(PipedReaderpr) Intruct uxurile care sunt conectate printr-un pipe trebuie s execute a a simultan operatii de scriere/citire, folosirea lor se va face din cadrul unor re de executie. Functionarea obicetelor care instantiaz PipedWriter i PipedReader a s este asemntoare cu a canalelor de comunicare UNIX (pipes). Fiecare capt a a a al unui canal este utilizat dintr-un r de executie separat. La un capt se a scriu caractere, la cellalt se citesc. La citire, dac nu sunt date disponibile a a rul de executie se va bloca pn ce acestea vor deveni disponibile. Se observ a a a c acesta este un comportament tipic productor-consumator asincron, rele a a de executie comunicnd printr-un canal. a Realizarea conexiunii se face astfel: PipedWriter PipedReader // sau PipedReader PipedWriter // sau pw1 = new PipedWriter(); pr1 = new PipedReader(pw1); pr2 = new PipedReader(); pw2 = new PipedWriter(pr2);

12.5. COMUNICAREA PRIN FLUXURI DE TIP PIPE PipedReader pr = new PipedReader(); PipedWriter pw = new PipedWirter(); pr.connect(pw) //echivalent cu pw.connect(pr);

377

Scrierea i citirea pe/de pe canale se realizeaz prin metodele uzuale read i s a s write, toate formele lor. n S reconsiderm acum exemplul productor/consumator prezentat antea a a rior, folosind canale de comunicatie. Productorul trimite datele printr-un a ux de ieire de tip DataOutputStream ctre consumator, care le primete s a s printr-un ux de intrare de tip DataInputStream. Aceste dou uxuri vor a interconectate prin intermediul unor uxuri de tip pipe. Listing 12.11: Folosirea uxurilor de tip pipe
import java . io .*; class Producator extends Thread { private DataOutputStream out ; public Producator ( DataOutputStream out ) { this . out = out ; } public void run () { for ( int i = 0; i < 10; i ++) { try { out . writeInt ( i ) ; } catch ( IOException e ) { e . printStackTrace () ; } System . out . println ( " Producatorul a pus :\ t " + i ) ; try { sleep (( int ) ( Math . random () * 100) ) ; } catch ( InterruptedE x c e p t i o n e ) { } } } } class Consumator extends Thread { private DataInputStream in ; public Consumator ( DataInputStream in ) { this . in = in ;

378
}

CAPITOLUL 12. FIRE DE EXECUTIE

public void run () { int value = 0; for ( int i = 0; i < 10; i ++) { try { value = in . readInt () ; } catch ( IOException e ) { e . printStackTrace () ; } System . out . println ( " Consumatorul a primit :\ t " + value ) ; } } }

public class TestPipes { public static void main ( String [] args ) throws IOException { PipedOutputStream pipeOut = new Pipe dOutput Stream () ; PipedInputStream pipeIn = new PipedInputStream ( pipeOut ) ; DataOutputStream out = new DataOutputStream ( pipeOut ) ; DataInputStream in = new DataInputStream ( pipeIn ) ; Producator p1 = new Producator ( out ) ; Consumator c1 = new Consumator ( in ) ; p1 . start () ; c1 . start () ; } }

12.6

Clasele Timer i TimerTask s

Clasa Timer ofer o facilitate de a planica diverse actiuni pentru a reala izate la un anumit moment de ctre un r de executie ce ruleaz fundal. a a n Actiunile unui obiect de tip Timer sunt implementate ca instante ale clasei TimerTask i pot programate pentru o singur executie sau pentru executii s a repetate la intervale regulate. Paii care trebuie fcuti pentru folosirea unui timer sunt: s a

12.6. CLASELE TIMER SI TIMERTASK

379

Crearea unei subclase Actiune a lui TimerTask i supreadenirea metodei s run ce va contine actiunea pe care vrem s o planicm. Dup cum a a a vom vedea, pot folosite i clase anonime. s Crearea unui r de executie prin instantierea clasei Timer; Crearea unui obiect de tip Actiune; Planicarea la executie a obiectuluii de tip Actiune, folosind metoda schedule din clasa Timer; Metodele de planicare pe care le avem la dispozitie au urmtoarele for a mate: schedule(TimerTask task, Date schedule(TimerTask task, long schedule(TimerTask task, Date scheduleAtFixedRate(TimerTask scheduleAtFixedRate(TimerTask time) delay, long period) time, long period) task, long delay, long period) task, Date time, long period)

unde, task descrie actiunea ce se va executa, delay reprezint arzierea fata a nt de momentul curent dup care va a ncepe executia, time momentul exact la care va ncepe executia iar period intervalul de timp ntre dou executii. a Dup cum se observ, metodele de planicare se a a mpart dou categorii: n a schedule - planicare cu arziere x: dac dintr-un anumit motiv nt a a actiunea este arziat, urmtoarele actiuni vor i ele arziate nt a a s nt n consecint; a scheduleAtFixedRate - planicare cu numr x de rate: dac dintra a un anumit motiv actiunea este arziat, urmtoarele actiuni vor nt a a executat mai repede, astfel at numrul total de actiuni dintr-o pea nc a rioad de timp s e tot timpul acelai; a a s Un timer se va opri natural la terminarea metodei sale run sau poate oprit fortat folosind metoda cancel. Dup oprirea sa el nu va mai putea a folosit pentru planicarea altor actiuni. De asemenea, metoda System.exit va oprit fortat toate rele de executie i va termina aplicatia curent. s a

380

CAPITOLUL 12. FIRE DE EXECUTIE Listing 12.12: Folosirea claselor Timer i TimerTask s

import java . util .*; import java . awt .*; class Atentie extends TimerTask { public void run () { Toolkit . getDefaultToolkit () . beep () ; System . out . print ( " . " ) ; } } class Alarma extends TimerTask { public String mesaj ; public Alarma ( String mesaj ) { this . mesaj = mesaj ; } public void run () { System . out . println ( mesaj ) ; } } public class TestTimer { public static void main ( String args []) { // Setam o actiune repetitiva , cu rata fixa final Timer t1 = new Timer () ; t1 . scheduleAtFix e d Ra t e ( new Atentie () , 0 , 1*1000) ; // Folosim o clasa anonima pentru o alta actiune Timer t2 = new Timer () ; t2 . schedule ( new TimerTask () { public void run () { System . out . println ( "S - au scurs 10 secunde . " ) ; // Oprim primul timer t1 . cancel () ; } } , 10*1000) ; // Setam o actiune pentru ora 22:30 Calendar calendar = Calendar . getInstance () ; calendar . set ( Calendar . HOUR_OF_DAY , 22) ; calendar . set ( Calendar . MINUTE , 30) ; calendar . set ( Calendar . SECOND , 0) ; Date ora = calendar . getTime () ;

12.6. CLASELE TIMER SI TIMERTASK

381

Timer t3 = new Timer () ; t3 . schedule ( new Alarma ( " Toti copiii la culcare ! " ) , ora ) ; } }

382

CAPITOLUL 12. FIRE DE EXECUTIE

Capitolul 13 Programare retea n


13.1 Introducere

Programarea retea implic trimiterea de mesaje i date n a s ntre aplicatii ce ruleaz pe calculatoare aate a ntr-o retea local sau conectate la Internet. a Pachetul care ofer suport pentru scrierea aplicatiilor de retea este java.net. a Clasele din acest pachet ofer o modalitate facil de programare retea, fr a a n aa a nevoie de cunotinte prealabile referitoare la comunicarea efectiv s a ntre calculatoare. Cu toate acestea, sunt necesare cteva notiuni fundamentale a referitoare la retele cum ar : protocol, adresa IP, port, socket.

Ce este un protocol ? Un protocol reprezint o conventie de reprezentare a datelor folosit comua a n nicarea ntre dou calculatoare. Avnd vedere faptul c orice informatie a a n a care trebuie trimis prin retea trebuie serializat astfel at s poat transa a nc a a mis secvential, octet cu octet, ctre destinatie, era nevoie de stabilirea unor a a conventii (protocoale) care s e folosite att de calculatorul care trimite a a datele ct i de cel care le primete, pentru a se elege a s s nt ntre ele. Dou dintre cele mai utilizate protocoale sunt TCP i UDP. a s TCP (Transport Control Protocol) este un protocol ce furnizeaz un a ux sigur de date ntre dou calculatoare aate retea. Acest protoa n col asigur stabilirea unei conexiuni permanente a ntre cele dou calcua latoare pe parcursul comunicatiei. UDP (User Datagram Protocol) este un protocol bazat pe pachete inde383

384

CAPITOLUL 13. PROGRAMARE RETEA IN pendente de date, numite datagrame, trimise de la un calculator ctre a altul fr a se garanta vreun fel ajungerea acestora la destinatie sau aa n ordinea care acestea ajung. Acest protocol nu stabilete o conexiun n s a permant a ntre cele dou calculatoare. a

Cum este identicat un calculator retea ? n Orice calculator conectat la Internet este identicat mod unic de adresa sa n IP (IP este acronimul de la Internet Protocol). Aceasta reprezint un numr a a reprezentat pe 32 de biti, uzual sub forma a 4 octeti, cum ar de exemplu: 193.231.30.131 i este numit adresa IP numeric. Corespunztoare unei s a a adrese numerice exista i o adresa IP simbolic, cum ar thor.infoiasi.ro s a pentru adresa numeric anterioar. a a De asemenea, ecare calculator aat ntr-o retea local are un nume unic a ce poat folosit la identicarea local a acestuia. a Clasa Java care reprezint notiunea de adres IP este InetAddress. a a

Ce este un port ? Un calculator are general o singur legtur zic la retea. Orice informatie n a a a a destinat unei anumite maini trebuie deci s specice obligatoriu adresa a s a IP a acelei maini. Ins pe un calculator pot exista concurent mai multe s a procese care au stabilite conexiuni retea, asteptnd diverse informatii. n a Prin urmare, datele trimise ctre o destinatie trebuie s specice pe lnga a a a adresa IP a calculatorului i procesul ctre care se s a ndreapt informatiile a respective. Identicarea proceselor se realizeaz prin intermdiul porturilor. a Un port este un numr pe 16 biti care identic mod unic procesele a a n care ruleaz pe o anumit masin. Orice aplicatie care realizeaz o conexiune a a a a retea va trebui s ataeze un numr de port acelei conexiuni. Valorile pe n a s a care le poate lua un numr de port sunt cuprinse a ntre 0 i 65535 (deoarece s sunt numere reprezentate pe 16 biti), numerele cuprinse ntre 0 i 1023 ind s a rezervate unor servicii sistem i, din acest motiv, nu trebuie folosite ns s n aplicatii.

Clase de baz din java.net a Clasele din java.net permit comunicarea ntre procese folosind protocoalele

13.2. LUCRUL CU URL-URI TCP i UDP i sunt prezentate tabelul de mai jos. s s n TCP URL URLConnection Socket ServerSocket UDP DatagramPacket DatagramSocket MulticastSocket

385

13.2

Lucrul cu URL-uri

Termenul URL este acronimul pentru Uniform Resource Locator i reprezint s a o referint (adres) la o resurs aat pe Internet. Aceasta este general un a a a a n ier reprezentnd o pagin Web, un text, imagine, etc., a un URL poate s a a ns referi i interogri la baze de date, rezultate ale unor comenzi executate la s a distanta, etc. Mai jost, sunt prezentate cteva exemple de URL-uri sunt: a http://java.sun.com http://students.infoiasi.ro/index.html http://www.infoiasi.ro/~acf/imgs/taz.gif http://www.infoiasi.ro/~acf/java/curs/9/prog_retea.html#url Dup cum se observ din exemplele de mai sus, un URL are dou coma a a ponente principale: Identicatorul protocolului folosit (http, ftp, etc); Numele resursei referite. Acesta are urmtoarele componente: a Numele calculatorului gazd (www.infoiasi.ro). a Calea complet spre resursa referit ( acf/java/curs/9/prog retea.html). a a Notatia user semnic uzual subdirectorul html al directoru a lui rezervat pe un server Web utilizatorului specicat (HOME). In cazul care este specicat doar un director, ierul ce reprezint n s a resursa va considerat implicit index.html. Optional, o referint de tip anchor cadrul ierului referit (#url). a n s Optional, portul la care s se realizeze conexiunea. a

386

CAPITOLUL 13. PROGRAMARE RETEA IN

Clasa care permite lucrul cu URL-uri este java.net.URL. Aceasta are mai multi constructori pentru crearea de obiecte ce reprezint referinte ctre a a resurse aate retea, cel mai uzual ind cel care primete ca parametru n s un ir de caractere. In cazul care irul nu reprezint un URL valid va s n s a aruncat o exceptie de tipul MalformedURLException. a try { URL adresa = new URL("http://xyz.abc"); } catch (MalformedURLException e) { System.err.println("URL invalid !\n" + e); } Un obiect de tip URL poate folosit pentru: Aarea informatiilor despre resursa referit (numele calculatorului gazd, a a numele ierului, protocolul folosit. etc). s Citirea printr-un ux a continutului ierului respectiv. s Conectarea la acel URL pentru citirea i scrierea de informatii. s

Citirea continutului unui URL Orice obiect de tip URL poate returna un ux de intrare de tip InputStream pentru citirea continutului su. Secventa standard pentru aceast operatiune a a este prezentat exemplul de mai jos, care aam continutul resursei a n n s specicat la linia de comand. Dac nu se specic mici un argument, va a a a a aat ierul index.html de la adresa: http://www.infoiasi.ro. s s Listing 13.1: Citirea continutului unui URL
import java . net .*; import java . io .*; public class CitireURL { public static void main ( String [] args ) throws IOException { String adresa = " http :// www . infoiasi . ro " ; if ( args . length > 0) adresa = args [0];

13.3. SOCKET-URI
BufferedReader br = null ; try { URL url = new URL ( adresa ) ; InputStream in = url . openStream () ; br = new BufferedReader ( new Inpu tStream Reader ( in ) ) ; String linie ; while (( linie = br . readLine () ) != null ) { // Afisam linia citita System . out . println ( linie ) ; } } catch ( Malfo rmedU RLEx c e p t i o n e ) { System . err . println ( " URL invalid !\ n " + e ) ; } finally { br . close () ; } } }

387

Conectarea la un URL Se realizeaz prin metoda openConnection ce stabilete o conexiune bidirectional a s a cu resursa specicat. Aceast conexiune este reprezentat de un obiect de a a a tip URLConnection, ce permite crearea att a unui ux de intrare pentru a citirea informatiilor de la URL-ul specicat, ct i a unui ux de ieire pentru a s s scrierea de date ctre acel URL. Operatiunea de trimitere de date dintr-un a program ctre un URL este similar cu trimiterea de date dintr-un formular a a de tip FORM aat ntr-o pagin HTML. Metoda folosit pentru trimitere este a a POST. In cazul trimiterii de date, obiectul URL este uzual un proces ce ruleaz a pe serverul Web referit prin URL-ul respectiv (jsp, servlet, cgi-bin, php, etc).

13.3

Socket-uri

Denitie Un socket (soclu) este o abstractiune software folosit pentru a reprezenta a ecare din cele dou capete ale unei conexiuni a ntre dou procese ce ruleaz a a ntr-o retea. Fiecare socket este ataat unui port astfel at s poat iden s nc a a tica unic programul cruia sunt destinate datele. a i

388

CAPITOLUL 13. PROGRAMARE RETEA IN

Socket-urile sunt de dou tipuri: a TCP, implementate de clasele Socket i ServerSocket; s UDP, implementate de clasa DatagramSocket. O aplicatie de retea ce folosete socket-uri se s ncadreaz modelul cliena n t/server de concepere a unei aplicatii. In acest model aplicatia este format a din dou categorii distincte de programe numite servere, respectiv clienti. a Programele de tip server sunt cele care ofer diverse servicii eventualilor a clienti, ind stare de ateptare atta vreme ct nici un client nu le solicit n s a a a serviciile. Programele de tip client sunt cele care initiaz conversatia cu un a server, solicitnd un anumit serviciu. Uzual, un server trebuie s e capabil s a a a trateze mai multi clienti simultan i, din acest motiv, ecare cerere adresat s a serverului va tratat a ntr-un r de executie separat.

Incepnd cu versiunea 1.4 a platformei standard Java, exist o clas utilia a a tar care implementeaz o pereche de tipul (adresa IP, numr port). Aceasta a a a este InetSocketAddress (derivat din SocketAddress), obiectele sale ind a utilizate de constructori i metode denite cadrul claselor ce descriu socks n eturi, pentru a specica cei doi parametri necesari identicrii unui proces a care trimite sau receptioneaz date retea. a n

13.4

Comunicarea prin conexiuni

In acest model se stabilete o conexiune TCP s ntre o aplicatie client i o s aplicatie server care furnizeaz un anumit serviciu. Avantajul protocolul a TCP/IP este c asigur realizarea unei comunicri stabile, permanente a a a n retea, existnd siguranta c informatiile trimise de un proces vor receptionate a a corect i complet la destinatie sau va semnalat o exceptie caz contrar. s a n Legtura a ntre un client i un server se realizeaz prin intermediul a s a dou obiecte de tip Socket, cte unul pentru ecare capt al canalului a a a de comunicatie dintre cei doi. La nivelul clientului crearea socketului se re alizeaz specicnd adresa IP a serverului i portul la care ruleaz acesta, a a s a constructorul uzual folosit ind: Socket(InetAddress address, int port)

13.4. COMUNICAREA PRIN CONEXIUNI

389

La nivelul serverului, acesta trebuie s creeze ai un obiect de tip a nt ServerSocket. Acest tip de socket nu asigur comunicarea efectiv cu a a clientii ci este responsabil cu ascultarea retelei i crearea unor obiecte de s tip Socket pentru ecare cerere aprut, prin intermediul cruia va reala a a izat legtura cu clientul. Crearea unui obiect de tip ServerSocket se face a a specicnd portul la care ruleaz serverul, constructorul folosit ind: a a ServerSocket(int port) Metoda clasei ServerSocket care ateapt ascult reteaua este accept. s a a Aceasta blocheaz procesul printe pn la aparitia unui cereri i returneaz a a a a s a un nou obiect de tip Socket ce va asigura comunicarea cu clientul. Blocarea poate s nu e permanent ci doar pentru o anumit perioad de timp a a a a aceasta va specicat prin metoda setSoTimeout, cu argumentul dat a n milisecunde.

Pentru ecare din cele dou socketuri deschise pot create apoi dou a a uxuri pe octeti pentru citirea, respectiv scrierea datelor. Acest lucru se re alizeaz prin intermediul metodelor getInputStream, respectuv getOuta putStream. Fluxurile obtinute vor folosite mpreun cu uxuri de procea sare care s asigure o comunicare facil a a ntre cele dou procese. In functie a de specicul aplicatiei acestea pot perechile: BufferedReader, BufferedWriter i PrintWriter - pentru comunis care prin intermediul irurilor de caractere; s DataInputStream, DataOutputStream - pentru comunicare prin date primitive; ObjectInputStream, ObjectOutputStream - pentru cominicare prin intermediul obiectelor;

390

CAPITOLUL 13. PROGRAMARE RETEA IN

Structura general a unui server bazat pe conexiuni este: a 1. Creeaza un obiect de tip ServerSocket la un anumit port while (true) { 2. Asteapta realizarea unei conexiuni cu un client, folosind metoda accept; (va fi creat un obiect nou de tip Socket) 3. Trateaza cererea venita de la client: 3.1 Deschide un flux de intrare si primeste cererea 3.2 Deschide un flux de iesire si trimite raspunsul 3.3 Inchide fluxurile si socketul nou creat } Este recomandat ca tratarea cererilor s se realizeze re de executie a n separate, pentru ca metoda accept s poat reapelat ct mai repede a a a a n vederea stabilirii conexiunii cu un alt client. Structura general a unui client bazat pe conexiuni este: a 1. Citeste sau declara adresa IP a serverului si portul la care acesta ruleaza; 2. Creeaza un obiect de tip Socket cu adresa si portul specificate; 3. Comunica cu serverul: 3.1 Deschide un flux de iesire si trimite cererea; 3.2 Deschide un flux de intrare si primeste raspunsul; 3.3 Inchide fluxurile si socketul creat; In exemplul urmtor vom implementa o aplicatie client-server folosind a comunicarea prin conexiuni. Clientul va trimite serverului un nume iar acesta va raspunde prin mesajul Hello nume. Tratarea cererilor se va face re n de executie separate. Listing 13.2: Structura unui server bazat pe conexiuni
import java . net .*; import java . io .*; class ClientThread extends Thread { Socket socket = null ; public ClientThread ( Socket socket ) { this . socket = socket ;

13.4. COMUNICAREA PRIN CONEXIUNI


} public void run () { // Executam solicitarea clientului String cerere , raspuns ; try { // in este fluxul de intrare de la client BufferedReader in = new BufferedReader ( new InputStreamReader ( socket . getInputStream () ) ) ; // out este flux de iesire catre client PrintWriter out = new PrintWriter ( socket . getOutputStream () ) ; // Primim cerere de la client cerere = in . readLine () ; // Trimitem raspuns clientului raspuns = " Hello " + cerere + " ! " ; out . println ( raspuns ) ; out . flush () ;

391

} catch ( IOException e ) { System . err . println ( " Eroare IO \ n " + e ) ; } finally { // Inchidem socketul deschis pentru clientul curent try { socket . close () ; } catch ( IOException e ) { System . err . println ( " Socketul nu poate fi inchis \ n " + e); } } } } public class SimpleServer { // Definim portul pe care se gaseste serverul // ( in afara intervalului 1 -1024) public static final int PORT = 8100; public SimpleServer () throws IOException { ServerSocket serverSocket = null ;

392

CAPITOLUL 13. PROGRAMARE RETEA IN


try { serverSocket = new ServerSocket ( PORT ) ; while ( true ) { System . out . println ( " Asteptam un client ... " ) ; Socket socket = serverSocket . accept () ; // Executam solicitarea clientului intr - un fir de executie ClientThread t = new ClientThread ( socket ) ; t . start () ; } } catch ( IOException e ) { System . err . println ( " Eroare IO \ n " + e ) ; } finally { serverSocket . close () ; }

} public static void main ( String [] args ) throws IOException { SimpleServer server = new SimpleServer () ; } }

Listing 13.3: Structura unui client bazat pe conexiuni


import java . net .*; import java . io .*; public class SimpleClient { public static void main ( String [] args ) throws IOException { // Adresa IP a serverului String adresaServer = " 127.0.0.1 " ; // Portul la care serverul ofera serviciul int PORT = 8100; Socket socket = null ; PrintWriter out = null ; BufferedReader in = null ; String cerere , raspuns ; try { socket = new Socket ( adresaServer , PORT ) ;

13.5. COMUNICAREA PRIN DATAGRAME

393

out = new PrintWriter ( socket . getOutputStream () , true ) ; in = new BufferedReader ( new Inpu tStream Reader ( socket . getInputStream () ) ) ; // Trimitem o cerere la server cerere = " Duke " ; out . println ( cerere ) ; // Asteaptam raspunsul de la server (" Hello Duke !") raspuns = in . readLine () ; System . out . println ( raspuns ) ; } catch ( UnknownHostExc e p t i o n e ) { System . err . println ( " Serverul nu poate fi gasit \ n " + e ) ; System . exit (1) ; } finally { if ( out != null ) out . close () ; if ( in != null ) in . close () ; if ( socket != null ) socket . close () ; } } }

13.5

Comunicarea prin datagrame

In acest model nu exist o conexiune permanent a a ntre client i server prin s intermediul creia s se realizeze comunicarea. Clientul trimite cererea ctre a a a server prin intermediul unuia sau mai multor pachete de date independente, serverul le receptioneaz, extrage informatiile continute i returneaz rspunsul a s a a tot prin intermediul pachetelor. Un astfel de pachet se numete datagram s a i este reprezentat printr-un obiect din clasa DatagramPacket. Rutarea s datagramelor de la o main la alta se face exclusiv pe baza informatiilor s a continute de acestea. Primirea i trimiterea datagramelor se realizeaz prin s a intermediul unui socket, modelat prin intermediul clasei DatagramSocket.

394

CAPITOLUL 13. PROGRAMARE RETEA IN

Dup cum am mentionat deja, dezavantajul acestei metode este c nu a a garanteaz ajungerea la destinatie a pachetelor trimise i nici c vor primite a s a aceeai ordinie care au fost expediate. Pe de alt parte, exist situatii n s n a a care aceste lucruri nu sunt importante i acest model este de preferat celui n s bazat pe conexiuni care solicit mult mai mult att serverul ct i clientul. De a a a s fapt, protocolul TCP/IP folosete tot pachete pentru trimiterea informatiilor s dintr-un nod altul al retelei, cu deosebirea c asigur respectarea ordinii de n a a transmitere a mesajelor i veric ajungerea la destinatie a tuturor pachetelor s a - cazul care unul nu a ajuns, acesta va retrimis automat. n n Clasa DatagramPacket contine urmtorii constructori: a DatagramPacket(byte[] buf, InetAddress address, int DatagramPacket(byte[] buf, InetAddress address, int int length, port) int offset, int length, port)

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) DatagramPacket(byte[] buf, int length, SocketAddress address) DatagramPacket(byte[] buf, int length) DatagramPacket(byte[] buf, int offset, int length) Primele dou perechi de constructori sunt pentru creare pachetelor ce a vor expediate, diferenta ntre ele ind utilizarea claselor InetAddress, respectiv SocketAddress pentru specicarea adresei desinatie. A trei pereche de constructori este folosit pentru crearea unui pachet a n care vor receptionate date, ei nespecicnd vreo surs sau destinatie. a a

13.5. COMUNICAREA PRIN DATAGRAME

395

Dup crearea unui pachet procesul de trimitere i primire a acestuia ima s plic apelul metodelor send i receive ale clasei DatagramSocket. Deoarece a s toate informatii sunt incluse datagram, acelai socket poate folosit att n a s a pentru trimiterea de pachete, eventual ctre destinatii diferite, ct i pentru a a s receptionarea acestora de la diverse surse. In cazul care refolosim pachete, n putem schimba continutul acestora cu metoda setData, precum i adresa la s care le trimitem prin setAddress, setPort i setSocketAddress. s Extragerea informatiilor contiunte de un pachet se realizeaz prin metoda a getData din clasa DatagramPacket. De asemenea, aceast clas ofer metode a a a pentru aarea adresei IP i a portului procesului care a trimis datagrama, s pentru a-i putea rspunde dac este necesar. Acestea sunt: getAdress, a a getPort i getSocketAddress. s Listing 13.4: Structura unui server bazat pe datagrame
import java . net .*; import java . io .*; public class DatagramServer { public static final int PORT = 8200; private DatagramSocket socket = null ; DatagramPacket cerere , raspuns = null ; public void start () throws IOException { socket = new DatagramSocket ( PORT ) ; try { while ( true ) { // Declaram pachetul in care va fi receptionata cererea byte [] buf = new byte [256]; cerere = new DatagramPacket ( buf , buf . length ) ; System . out . println ( " Asteptam un pachet ... " ) ; socket . receive ( cerere ) ; // Aflam adresa si portul de la care vine cererea InetAddress adresa = cerere . getAddress () ; int port = cerere . getPort () ; // Construim raspunsul

396

CAPITOLUL 13. PROGRAMARE RETEA IN


String mesaj = " Hello " + new String ( cerere . getData () ); buf = mesaj . getBytes () ; // Trimitem un pachet cu raspunsul catre client raspuns = new DatagramPacket ( buf , buf . length , adresa , port ) ; socket . send ( raspuns ) ; } } finally { if ( socket != null ) socket . close () ; }

} public static void main ( String [] args ) throws IOException { new DatagramServer () . start () ; } }

Listing 13.5: Structura unui client bazat pe datagrame


import java . net .*; import java . io .*; public class DatagramClient { public static void main ( String [] args ) throws IOException { // Adresa IP si portul la care ruleaza serverul InetAddress adresa = InetAddress . getByName ( " 127.0.0.1 " ) ; int port =8200; DatagramSocket socket = null ; DatagramPacket packet = null ; byte buf []; try { // Construim un socket pentru comunicare socket = new DatagramSocket () ; // Construim si trimitem pachetul cu cererea catre server buf = " Duke " . getBytes () ;

13.6. TRIMITEREA DE MESAJE CATRE MAI MULTI CLIENTI


packet = new DatagramPacket ( buf , buf . length , adresa , port ) ; socket . send ( packet ) ; // Asteaptam pachetul cu raspunsul de la server buf = new byte [256]; packet = new DatagramPacket ( buf , buf . length ) ; socket . receive ( packet ) ; // Afisam raspunsul (" Hello Duke !") System . out . println ( new String ( packet . getData () ) ) ; } finally { if ( socket != null ) socket . close () ; } } }

397

13.6

Trimiterea de mesaje ctre mai multi a clienti

Diverse situatii impun gruparea mai multor clienti astfel at un mesaj (pa nc chet) trimis pe adresa grupului s e receptionat de ecare dintre acetia. a s Gruparea mai multor programe vederea trimiterii multiple de mesaje se n realizeaz prin intermediul unui socket special, descris de clasa Multicasta Socket, extensie a clasei DatagramSocket. Un grup de clienti abonati pentru trimitere multipl este specicat printr a o adres IP din intervalul 224.0.0.1 - 239.255.255.255 i un port UDP. a s Adresa 224.0.0.0 este rezervat i nu trebuie folosit. as a

398

CAPITOLUL 13. PROGRAMARE RETEA IN

Listing 13.6: Inregistrarea unui client ntr-un grup


import java . net .*; import java . io .*; public class MulticastClient { public static void main ( String [] args ) throws IOException { // Adresa IP si portul care reprezinta grupul de clienti InetAddress group = InetAddress . getByName ( " 230.0.0.1 " ) ; int port =4444; MulticastSocket socket = null ; byte buf []; try { // Ne alaturam grupului aflat la adresa si portul specificate socket = new MulticastSocket ( port ) ; socket . joinGroup ( group ) ; // Asteaptam un pachet venit pe adresa grupului buf = new byte [256]; DatagramPacket packet = new DatagramPacket ( buf , buf . length ) ; System . out . println ( " Asteptam un pachet ... " ) ; socket . receive ( packet ) ;

13.6. TRIMITEREA DE MESAJE CATRE MAI MULTI CLIENTI

399

System . out . println ( new String ( packet . getData () ) . trim () ) ; } finally { if ( socket != null ) { socket . leaveGroup ( group ) ; socket . close () ; } } } }

Listing 13.7: Transmiterea unui mesaj ctre un grup a


import java . net .*; import java . io .*; public class MulticastSend { public static void main ( String [] args ) throws IOException { InetAddress grup = InetAddress . getByName ( " 230.0.0.1 " ) ; int port = 4444; byte [] buf ; DatagramPacket packet = null ; // Cream un socket cu un numar oarecare DatagramSocket socket = new DatagramSocket (0) ; try { // Trimitem un pachet catre toti clientii din grup buf = ( new String ( " Salut grup ! " ) ) . getBytes () ; packet = new DatagramPacket ( buf , buf . length , grup , port ); socket . send ( packet ) ; } finally { socket . close () ; } } }

400

CAPITOLUL 13. PROGRAMARE RETEA IN

Capitolul 14 Appleturi
14.1 Introducere

Denitie Un applet reprezint un program Java de dimensiuni reduse ce gestioneaz a a o suprafat de aare (container) care poate inclus a s a ntr-o pagin Web. Un a astfel de program se mai numete miniaplicatie. s Ca orice alt aplicatie Java, codul unui applet poate format din una sau a mai multe clase. Una dintre acestea este principal i extinde clasa Applet, as aceasta ind clasa ce trebuie specicat documentul HTML ce descrie a n pagina Web care dorim s includem appletul. n a Diferenta fundamental dintre un applet i o aplicatie const faptul c a s a n a un applet nu poate executat independent, ci va executat de browserul care este arcat pagina Web ce contine appletul respectiv. O aplicatie n nc a independent este executat prin apelul interpretorului java, avnd ca ara a a gument numele clasei principale a aplicatiei, clasa principal ind cea care a contine metoda main. Ciclul de viat al unui applet este complet diferit, a ind dictat de evenimentele generate de ctre browser la vizualizarea docua mentului HTML ce contine appletul. Pachetul care ofer suport pentru crearea de appleturi este java.applet, a cea mai important clas ind Applet. In pachetul javax.swing exist a a a i clasa JApplet, care extinde Applet, oferind suport pentru crearea de s appleturi pe arhitectura de componente JFC/Swing. 401

402

CAPITOLUL 14. APPLETURI

Ierarhia claselor din care deriv appleturile este prezentata gura de a n mai jos:

Fiind derivat din clasa Container, clasa Applet descrie de fapt suprafete a de aare, asemenea claselor Frame sau Panel. s

14.2

Crearea unui applet simplu

Crearea structurii de iere i compilarea applet-urilor sunt identice ca s s n cazul aplicatiilor. Difer schimb structura programului i modul de rulare a n s a acestuia. S parguream continuare aceti pai pentru a realiza un applet a n s s extrem de simplu, care aeaz o imagine i un ir de caractere. s a s s

1. Scrierea codului sursa

import java.awt.* ; import java.applet.* ; public class FirstApplet extends Applet { Image img; public void init() { img = getImage(getCodeBase(), "taz.gif"); }

14.2. CREAREA UNUI APPLET SIMPLU public void paint (Graphics g) { g.drawImage(img, 0, 0, this); g.drawOval(100,0,150,50); g.drawString("Hello! My name is Taz!", 110, 25); } }

403

Pentru a putea executat de browser, clasa principal a appletului trea a buie s e public. a a

2. Salvarea sierelor surs a Ca orice clas public, clasa principala a appletului va salvat a a a ntr-un ier cu acelai nume i extensia .java. Aadar, vom salva clasa de mai sus s s s s ntr-un ier FirstApplet.java. s

3. Compilarea Compilarea se face la fel ca i la aplicatiile independente, folosind compis latorul javac apelat pentru ierul ce contine appletul. s javac FirstApplet.java In cazul care compilarea a reuit va generat sierul FirstApplet.class. n s

4. Rularea appletului Applet-urile nu ruleaza independent. Ele pot rulate doar prin intermediul unui browser: Internet Explorer, Netscape, Mozilla, Opera, etc. sau printr-un program special cum ar appletviewer din kitul de dezvoltare J2SDK. Pentru a executa un applet trebuie s facem dou operatii: a a Crearea unui ier HTML care vom include applet-ul. S cons n a siderm ierul simplu.html, avnd continutul de mai jos: a s a

404

CAPITOLUL 14. APPLETURI <html> <head> <title>Primul applet Java</title> </head> <body> <applet code=FirstApplet.class width=400 height=400> </applet> </body> </html>

Vizualizarea appletului: se deschide sierul simplu.html folosind unul din browser-ele amintite sau efectund apelul: a appletviewer simplu.html.

14.3

Ciclul de viat al unui applet a

Executia unui applet ncepe momentul care un browser aeaz o pagin n n s a a Web care este inclus appletul respectiv i poate trece prin mai multe etape. n s Fiecare etap este strns legat de un eveniment generat de ctre browser i a a a a s determin apelarea unei metode specice din clasa ce implementeaz applea a tul. Incrcarea memorie a n Este creat o instanta a clasei principale a appletului i a s ncarcat a n memorie. Initializarea Este apelat metoda init ce permite initializarea diverselor variabile, a citirea unor parametri de intrare, etc. Pornirea Este apelat metoda start a Executia propriu-zis a Const interactiunea dintre utilizator i componentele aate pe a n s s suprafata appletului sau executarea unui anumit cod n ntr-un r de executie. In unele situatii ntreaga executie a appletului se consum la a etapele de initializare i pornire. s

14.3. CICLUL DE VIATA AL UNUI APPLET

405

Oprirea temporar a In cazul care utilizatorul prsete pagina Web care ruleaz applen aa s n a tul este apelat metoda stop a acestuia, dndu-i astfel posibilitatea s a a a opreasca temporar executia sa pe perioada care nu este vizibil, pentru n a nu consuma inutil din timpul procesorului. Acelai lucru se ampl s nt a dac fereastra browserului este minimizat. In momentul cnd pagina a a a Web ce contine appletul devine din nou activ, va reapelat metoda a a start. Oprirea denitiv a La nchiderea tuturor instantelor browserului folosit pentru vizualizare, appletul va eliminat din memorie i va apelat metoda destroy a s a acestuia, pentru a-i permite s elibereze resursele detinute. Apelul a metodei destroy este ntotdeauna precedat de apelul lui stop.

Metodele specice appleturilor Aadar, exist o serie de metode specice appleturilor ce sunt apelate aus a tomat la diverse evenimente generate de ctre browser. Acestea sunt denite a clasa Applet i sunt enumerate tabelul de mai jos: n s n Metoda Situatia care este apelat n a init La initializarea appletului. Teoretic, aceast metod a a ar trebui s se apeleze o singur dat, la prima a a a aare a appletului pagin, a, la unele browsere, s n a ns este posibil ca ea s se apeleze de mai multe ori. a start Imediat dup initializare i de ecare dat a s a cnd appletul redevine activ, dup o oprire temporar. a a a stop De ecare dat cnd appletul nu mai este vizibil a a (pagina Web nu mai este vizibil, fereastra browserului a este minimizat, etc) i a s nainte de destroy. destroy La nchiderea ultimei instante a browserului care a arcat memorie clasa principal a appletului. nc n a

Atentie

406

CAPITOLUL 14. APPLETURI

Aceste metode sunt apelate automat de browser i nu trebuie apelate s explicit din program !

Structura general a unui applet a import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class StructuraApplet extends Applet { public void init() { } public void start() { } public void stop() { } public void destroy() { } }

14.4

Interfata grac cu utilizatorul a

Dup cum am vzut, clasa Applet este o extensie a superclasei Container, a a ceea ce nseamn c appleturile sunt, a a nainte de toate, suprafete de aare. s Plasarea componentelor, gestionarea pozitionrii lor i tratarea evenimentelor a s generate se realizeaz la fel ca i cazul aplicatiilor. Uzual, adugarea coma s n a ponentelor pe suprafata appletului precum i stabilirea obiectelor respons s abile cu tratarea evenimentelor generate sunt operatiuni ce vor realizate n metoda init.

14.4. INTERFATA GRAFICA CU UTILIZATORUL

407

Gestionarul de pozitionare implicit este FlowLayout, a acesta poate ns schimbat prin metoda setLayout.

408

CAPITOLUL 14. APPLETURI

Desenarea pe suprafata unui applet Exist o categorie a ntreag de appleturi ce nu comunic cu utilizatorul a a prin intermediul componentelor ci, executia lor se rezum la diverse operatiuni a de desenare realizate metoda paint. Reamintim c metoda paint este ren a sponsabil cu denirea aspectului grac al oricrei componente. Implicit, a a metoda paint din clasa Applet nu realizeaz nimic, deci, cazul care a n n dorim s desenm direct pe suprafata unui applet va nevoie s supradenim a a a aceast metod. a a public void paint(Graphics g) { // Desenare ... } In cazul care este aleas aceast solutie, evenimentele tratate uzual vor n a a cele generate de mouse sau tastatur. a

14.5

Denirea i folosirea parametrilor s

Parametrii sunt pentru appleturi ceea ce argumentele de la linia de comand a sunt pentru aplicatiile independente. Ei permit utilizatorului s personalizeze a aspectul sau comportarea unui applet fr a-i schimba codul i recompila aa s clasele. Denirea parametrilor se face cadrul tagului APPLET din documenn tul HTML ce contine appletul i sunt identicati prin atributul PARAM. s Fiecare parametru are un nume, specicat prin NAME i o valoare, specis cat prin VALUE, ca exemplul de mai jos: a n <APPLET CODE="TestParametri.class" WIDTH=100 HEIGHT=50 <PARAM NAME=textAfisat VALUE="Salut"> <PARAM NAME=numeFont VALUE="Times New Roman"> <PARAM NAME=dimFont VALUE=20> </APPLET> Ca i cazul argumentelor trimise aplicatiilor de la linia de comand, tipul s n a parametrilor este ntotdeauna ir de caractere, indiferent dac valoarea s a este ntre ghilimele sau nu. Fiecare applet are i un set de parametri prestabiliti ale cror nume nu s a vor putea folosite pentru denirea de noi parametri folosind metoda de

14.5. DEFINIREA SI FOLOSIREA PARAMETRILOR

409

mai sus. Acetia apar direct corpul tagului APPLET i denesc informatii s n s generale despre applet. Exemple de astfel de parametri sunt CODE, WIDTH sau HEIGHT. Lista lor complet va prezentata la descrierea tagului APPLET. a Folosirea parametrilor primiti de ctre un applet se face prin intermediul a metodei getParameter care primete ca argument numele unui parametru s i returneaz valoarea acestuia. In cazul care nu exist nici un parametru s a n a cu numele specicat, metoda ntoarce null, caz care programul trebuie n s atribuie o valoare implicit variabilei care se dorea citirea respectivului a a n parametru. Orice applet poate pune la dispozitie o documentatie referitoare la para metrii pe care suport, pentru a veni ajutorul utilizatorilor care doresc s i a n a includ appletul a ntr-o pagin Web. Aceasta se realizeaz prin supradenirea a a metodei getParameterInfo, care returneaz un vector format din triplete a de iruri. Fiecare element al vectorului este de fapt un vector cu trei elemente s de tip String, cele trei iruri reprezentnd numele parametrului, tipul su i s a a s o descriere a sa. Informatiile furnizate de un applet pot citite din browserul folosit pentru vizualizare prin metode specice acestuia. De exemplu, apn pletviewer informatiile despre parametri pot vizualizate la rubrica Info din meniul Applet, Netscape se folosete optiunea Page info din meniul View, n s etc. S scriem un applet care s aeze un text primit ca parametru, folosind a a s un font cu numele i dimensiunea specicate de asemenea ca parametri. s Listing 14.1: Folosirea parametrilor
import java . applet . Applet ; import java . awt .*; public class TestParametri extends Applet String text , numeFont ; int dimFont ; public void init () { text = getParameter ( " textAfisat " ) ; if ( text == null ) text = " Hello " ; // valoare implicita numeFont = getParameter ( " numeFont " ) ; if ( numeFont == null ) numeFont = " Arial " ; {

410

CAPITOLUL 14. APPLETURI

try { dimFont = Integer . parseInt ( getParameter ( " dimFont " ) ) ; } catch ( Numb erFo r m at Ex ce pt i o n e ) { dimFont = 16; } } public void paint ( Graphics g ) { g . setFont ( new Font ( numeFont , Font . BOLD , dimFont ) ) ; g . drawString ( text , 20 , 20) ; } public String [][] getParameterInfo () { String [][] info = { // Nume Tip Descriere { " textAfisat " , " String " , " Sirul ce va fi afisat " } , { " numeFont " , " String " , " Numele fontului " } , { " dimFont " , " int " , " Dimensiunea fontului " } }; return info ; } }

14.6

Tag-ul APPLET

Sintaxa complet a tagului APPLET, cu ajutorul cruia pot incluse applea a turi cadrul paginilor Web este: n <APPLET CODE = clasaApplet WIDTH = latimeInPixeli HEIGHT = inaltimeInPixeli [ARCHIVE = arhiva.jar] [CODEBASE = URLApplet] [ALT = textAlternativ] [NAME = numeInstantaApplet] [ALIGN = aliniere] [VSPACE = spatiuVertical]

14.6. TAG-UL APPLET [HSPACE = spatiuOrizontal] > [< PARAM NAME = parametru1 VALUE = valoare1 >] [< PARAM NAME = parametru2 VALUE = valoare2 >] ... [text HTML alternativ] </APPLET> Atributele puse ntre paranteze ptrate sunt optionale. a

411

CODE = clasaApplet Numele ierului ce contine clasa principal a appletului. Acesta va s a cutat directorul specicat de CODEBASE. Nu poate absolut i a n s trebuie obligatoriu specicat. Extensia .class poate sau nu s apar. a a WIDTH =latimeInPixeli, HEIGHT =inaltimeInPixeli Specic limea i altimea suprafetei care va aat appletul. a at s n n s Sunt obligatorii. ARCHIVE = arhiva.jar Specic arhiva care se gsesc clasele appletului. a n a CODEBASE = directorApplet Specic URL-ul la care se gsete clasa appletului. Uzual se exprim a a s a relativ la directorul documentului HTML. In cazul care lipsete, se n s consider implicit URL-ul documentului. a ALT = textAlternativ Specic textul ce trebuie aat dac browserul elege tagul APPLET a s a nt dar nu poate rula appleturi Java. NAME =numeInstantaApplet Ofer posibilitatea de a da un nume respectivei instante a appletua lui, astfel at mai multe appleturi aate pe aceeai pagin s poat nc s a a a comunica ntre ele folosindu-se de numele lor. ALIGN =aliniere Semnic modalitatea de aliniere a appletului pagina Web. Acest a n atribut poate primi una din urmtoarele valori: left, right, top, a

412

CAPITOLUL 14. APPLETURI texttop, middle, absmiddle, baseline, bottom, absbottom , seminicatiile lor ind aceleai ca i la tagul IMG. s s

VSPACE =spatiuVertical, HSPACE = spatiuOrizontal Specic numarul de pixeli dintre applet i marginile suprafetei de a s aare. s PARAM Tag-urile PARAM sunt folosite pentru specicarea parametrilor unui applet (vezi Folosirea parametrilor). text HTML alternativ Este textul ce va aat cazul care browserul nu s n n ntelege tagul APPLET. Browserele Java-enabled vor ignora acest text.

14.7

Folosirea relor de executie appleturi n

La arcarea unei pagini Web, ecrui applet este creat automat un r nc a i de executie responsabil cu apelarea metodelor acestuia. Acestea vor rula concurent dup regulile de planicare implementate de maina virtual Java a s a a platformei folosite. Din punctul de vedere al interfetei grace a, ecare applet aat pe o ns pagin Web are acces la un acelai r de executie, creat de asemenea automat a s de ctre browser, i care este responsabil cu desenarea appletului (apelul a s metodelor update i paint) precum i cu transmiterea mesajelor generate s s de ctre componente. Intruct toate appleturile de pe pagin a a a mpart acest r de executie, nici unul nu trebuie s solicite mod excesiv, deoarece va a l n provoca functionarea anormal sau chiar blocarea celorlalte. a In cazul care dorim s efectum operatiuni consumatoare de timp este n a a recomandat s le realizm a a ntr-un alt r de executie, pentru a nu bloca interactiunea utilizatorului cu appletul, redesenarea acestuia sau activitatea celorlalte appleturi de pe pagin. a S considermm mai ai dou abordri greite de lucru cu appleturi. a a nt a a s Dorim s crem un applet care s aeze la coordonate aleatoare mesajul a a a s Hello, cu pauz de o secund a a ntre dou ari. Prima variant, greit de a sa a s a altfel, ar :

14.7. FOLOSIREA FIRELOR DE EXECUTIE APPLETURI IN Listing 14.2: Incorect: blocarea metodei paint
import java . applet .*; import java . awt .*; public class AppletRau1 extends Applet { public void paint ( Graphics g ) { while ( true ) { int x = ( int ) ( Math . random () * getWidth () ) ; int y = ( int ) ( Math . random () * getHeight () ) ; g . drawString ( " Hello " , x , y ) ; try { Thread . sleep (1000) ; } catch ( InterruptedE xc ep ti on e ) {} } } }

413

Motivul pentru care acest applet nu functioneaz corect i probabil va a s duce la anomalii functionarea browserului este c rul de executie care n a se ocup cu desenarea va rmne blocat metoda paint, a a a n ncercnd s o a a termine. Ca regul general, codul metodei paint trebuie s e ct mai a a a a simplu de executat ceea ce, evident, nu este cazul appletul de mai sus. n O alt idee de rezolvare care ne-ar putea veni, de asemenea greit, este a s a urmtoarea : a Listing 14.3: Incorect: appletul nu termin initializarea a
import java . applet .*; import java . awt .*; public class AppletRau2 extends Applet { int x , y ; public void init () { while ( true ) { x = ( int ) ( Math . random () * getWidth () ) ; y = ( int ) ( Math . random () * getHeight () ) ; repaint () ; try { Thread . sleep (1000) ; } catch ( InterruptedE x ce pt io n e ) {} } } public void paint ( Graphics g ) {

414
g . drawString ( " Hello " , x , y ) ; } }

CAPITOLUL 14. APPLETURI

Pentru a putea da o solutie corect problemei propuse, trebuie s folosim a a un r de executie propriu. Structura unui applet care doreste s lanseze un a r de executie poate avea dou forme. In prima situatie appletul pornete a s rul la initialzarea sa iar acesta va rula, indiferent dac appletul mai este sau a nu vizibil, pn la oprirea sa natural (terminarea metodei run) sau pn la a a a a a nchiderea sesiunii de lucru a browserului. Listing 14.4: Corect: folosirea unui r de executie propriu
import java . applet .*; import java . awt .*; public class AppletCorect1 extends Applet implements Runnable { int x , y ; Thread fir = null ; public void init () { if ( fir == null ) { fir = new Thread ( this ) ; fir . start () ; } } public void run () { while ( true ) { x = ( int ) ( Math . random () * getWidth () ) ; y = ( int ) ( Math . random () * getHeight () ) ; repaint () ; try { Thread . sleep (1000) ; } catch ( Inter ru pt ed Ex ce pt io n e ) {} } } public void paint ( Graphics g ) { g . drawString ( " Hello " , x , y ) ; } }

In cazul care rul de executie pornit de applet efectueaz operatii ce n a

14.7. FOLOSIREA FIRELOR DE EXECUTIE APPLETURI IN

415

au sens doar dac appletul este vizibil, cum ar animatie, ar de dorit a ca acesta s se opreasca atunci cnd appletul nu mai este vizibil (la apelul a a metodei stop) i s reporneasca atunci cnd appletul redevine vizibil (la s a a apelul metodei start). Un applet este considerat activ imediat dup apelul a metodei start i devine inactiv la apelul metodei stop. Pentru a aa dac s a un applet este activ se folosete metoda isActive. s S modicm programul anterior, adugnd i un contor care s numere a a a a s a arile de mesaje - acesta nu va incrementat pe perioada care appletul sa n nu este activ. Listing 14.5: Folosirea metodelor start i stop s
import java . applet .*; import java . awt .*; public class AppletCorect2 extends Applet implements Runnable { int x , y ; Thread fir = null ; boolean activ = false ; int n = 0; public void start () { if ( fir == null ) { fir = new Thread ( this ) ; activ = true ; fir . start () ; } } public void stop () { activ = false ; fir = null ; } public void run () { while ( activ ) { x = ( int ) ( Math . random () * getWidth () ) ; y = ( int ) ( Math . random () * getHeight () ) ; n ++; repaint () ; try { Thread . sleep (1000) ; } catch ( InterruptedE x c e p t i o n e ) {}

416
} }

CAPITOLUL 14. APPLETURI

public void paint ( Graphics g ) { g . drawString ( " Hello " + n , x , y ) ; } }

Atentie Este posibil ca unele browsere s nu apele metoda stop situatiile a n prevzute specciatiile appleturilor. Din acest motiv, corectitudinea unui a n applet nu trebuie s se bazeze pa acest mecanism. a

14.8

Alte metode oferite de clasa Applet

Pe lng metodele de baz: init, start, stop, destroy, clasa Applet a a a ofer metode specice applet-urilor cum ar : a

Punerea la dispozitie a unor informatii despre applet Similar cu metoda getParameterInfo ce oferea o documentatie despre a parametrii pe care accept un applet, exist metoda getAppletInfo ce peri a a mite specicarea unor informatii legate de applet cum ar numele, autorul, versiunea, etc. Metoda returneaz un sir de caractere continnd informatiile a a respective. public String getAppletInfo() { return "Applet simplist, autor necunoscut, ver 1.0"; }

Aarea adreselor URL referitoare la applet Se realizeaz cu metodele: a

14.8. ALTE METODE OFERITE DE CLASA APPLET

417

getCodeBase - ce returneaz URL-ul directorului ce contine clasa a appletului; getDocumentBase - returneaz URL-ul directorului ce contine doca umentul HTML care este inclus appletul respectiv. n Aceste metode sunt foarte utile deoarece permit specicarea relativ a unor a iere folosite de un applet, cum ar imagini sau sunete. s

Aarea unor mesaje bara de stare a browserului s n Acest lucru se realizeaz cu metoda showStatus a public void init() { showStatus("Initializare applet..."); }

Aarea imaginilor s Aarea imaginilor s ntr-un applet se face e prin intermediul unei componente ce permite acest lucru, cum ar o suprafat de desenare de tip Canvas, a e direct metoda paint a applet-ului, folosind metoda drawImage a clasei n Graphics. In ambele cazuri, obtinerea unei referinte la imaginea respectiv a se va face cu ajutorul metodei getImage din clasa Applet. Aceasta poate primi ca argument e adresa URL absolut a ierului ce reprezint imaga s a inea, e calea relativ la o anumit adres URL, cum ar cea a directorului a a a care se gsete documentul HTML ce contine appletul (getDocumentBase) n a s sau a directorului care se gsete clasa appletului (getCodeBase). n a s Listing 14.6: Aarea imaginilor s
import java . applet . Applet ; import java . awt .*; public class Imagini extends Applet Image img = null ; {

public void init () { img = getImage ( getCodeBase () , " taz . gif " ) ; }

418

CAPITOLUL 14. APPLETURI

public void paint ( Graphics g ) { g . drawImage ( img , 0 , 0 , this ) ; } }

Aarea contextului de executie Contextul de executie al unui applet se refer la pagina care acesta ruleaz, a n a eventual mpreun cu alte appleturi, i este descris de interfata AppletCona s text. Crearea unui obiect ce implementeaz aceast interfat se realizeaz de a a a a ctre browser, la apelul metodei getAppletContext a clasei Applet. Prin a intermediul acestei interfete un applet poate vedea jurul sau, putnd n a comunica cu alte applet-uri aate pe aceeasi pagin sau cere browser-ului s a a deschid diverse documente. a AppletContext contex = getAppletContext();

Aarea unor documente browser s n Se face cu metoda showDocument ce primete adresa URL a ierului ce s s contine documentul pe care dorim sa-l deschidem (text, html, imagine, etc). Aceast metod este accesat prin intermediul contextului de executie al a a a appletului. try { URL doc = new URL("http://www.infoiasi.ro"); getAppletContext().showDocument(doc); } catch(MalformedURLException e) { System.err.println("URL invalid! \n" + e); }

Comunicarea cu alte applet-uri Aceast comunicare implic de fapt identicarea unui applet aat pe aceeai a a s pagina i apelarea unei metode sau setarea unei variabile publice a acestuia. s Identicarea se face prin intermediu numelui pe care orice instanta a unui applet poate specica prin atributul NAME. l

14.8. ALTE METODE OFERITE DE CLASA APPLET

419

Obtinerea unei referinte la un applet al crui nume cunoatem sau a l s obtinerea unei enumerri a tuturor applet-urilor din pagin se fac prin in a a termediul contextului de executie, folosind metodele getApplet, respectiv getApplets.

Redarea sunetelor Clasa Applet ofer i posibilitatea redrii de sunete format .au. Acestea as a n sunt descrise prin intermediul unor obiecte ce implementeaz interfata Aua dioClip din pachetul java.applet. Pentru a reda un sunet aat ntr-un ier .au la un anumit URL exist dou posibiliti: s a a at Folosirea metodei play din clasa Applet care primete ca argument s URL-ul la care se a sunetul; acesta poate specicat absolut sau a relativ la URL-ul appletului Crearea unui obiect de tip AudioClip cu metoda getAudioClip apoi apelarea metodelor start, loop i stop pentru acesta. s

Listing 14.7: Redarea sunetelor


import java . applet .*; import java . awt .*; import java . awt . event .*; public class Sunete extends Applet implements ActionListener { Button play = new Button ( " Play " ) ; Button loop = new Button ( " Loop " ) ; Button stop = new Button ( " Stop " ) ; AudioClip clip = null ; public void init () { // Fisierul cu sunetul trebuie sa fie in acelasi // director cu appletul clip = getAudioClip ( getCodeBase () , " sunet . au " ) ; add ( play ) ; add ( loop ) ; add ( stop ) ; play . addActionListener ( this ) ; loop . addActionListener ( this ) ; stop . addActionListener ( this ) ;

420
}

CAPITOLUL 14. APPLETURI

public void actionPerformed ( ActionEvent e ) { Object src = e . getSource () ; if ( src == play ) clip . play () ; else if ( src == loop ) clip . loop () ; else if ( src == stop ) clip . stop () ; } }

In cazul care appletul folosete mai multe tipuri de sunete, este recon s mandat ca arcarea acestora s e fcut nc a a a ntr-un r de executie separat, pentru a nu bloca temporar activitatea reasc a programului. a

14.9

Arhivarea appleturilor

Dup cum am vzut, pentru ca un applet aat pe o pagin Web s poat a a a a a executat codul su va transferat de pe serverul care gzduiete pagina a a s Web solicitat pe maina clientului. Deoarece transferul datelor prin retea a s este un proces lent, cu ct dimensiunea ierlor care formeaz appletul este a s a mai redus, cu at arcarea acestuia se va face mai repede. Mai mult, dac a a nc a appletul contine i alte clase afar de cea principal sau diverse resurse s n a a (imagini, sunete, etc), acestea vor transferate prin retea abia momentul n care va nevoie de ele, oprind temporar activitatea appletului pn la n a a arcarea lor. Din aceste motive, cea mai ecient modalitate de a distribui nc a un applet este s arhivm toate ierele necesare acestuia. a a s Arhivarea ierelor unui applet se face cu utilitarul jar, oferit s n distributia J2SDK. // Exemplu jar cvf arhiva.jar ClasaPrincipala.class AltaClasa.class imagine.jpg sunet.au // sau jar cvf arhiva.jar *.class *.jpg *.au

14.10. RESTRICTII DE SECURITATE

421

Includerea unui applet arhivat ntr-o pagin Web se realizeaz spea a cicnd pe lng numele clasei principale i numele arhivei care o contine: a a a s <applet archive=arhiva.jar code=ClasaPrincipala width=400 height=200 />

14.10

Restrictii de securitate

Deoarece un applet se execut pe maina utilizatorului care a solicitat paga s ina Web ce contine appletul respectiv, este foarte important s existe anu a mite restrictii de securitate care s controleze activitatea acestuia, pentru a a preveni actiuni ru intentionate, cum ar tergeri de iere, etc., care s a s s a aduc prejudicii utilizatorului. Pentru a realiza acest lucru, procesul care a ruleaz appleturi instaleaz un manager de securitate, adic un obiect de a a a tip SecurityManager care va superviza activitatea metodelor appletului, aruncnd exceptii de tip Security Exception cazul care una din acesa n n tea ncearc s efectueze o operatie nepermis. a a a Un applet nu poate s: a Citeasc sau s scrie iere pe calculatorul pe care a fost a a s ncarcat (client). Deschid conexiuni cu alte maini afar de cea de pe care provine a s n a (host). Porneasc programe pe maina client. a s Citeasc diverse proprieti ale sistemului de operare al clientului. a at Ferestrele folosite de un applet, altele dect cea a browserului, vor arta a a altfel dect a ntr-o aplicatie obinuit, indicnd faptul c au fost create de un s a a a applet.

14.11

Appleturi care sunt i aplicatii s

Deoarece clasa Applet este derivat din Container, deci i din Component, a s ea descrie o suprafat de aare care poate inclus ca orice alt component a s a a a ntr-un alt container, cum ar o fereastr. Un applet poate functiona i ca a s o aplicatie independent astfel: a

422

CAPITOLUL 14. APPLETURI

Adugm metoda main clasei care descrie appletul, care vom face a a n operatiunile urmtoare. a Crem o instant a appletului i o adugm pe suprafata unei ferestre. a a s a a Apelm metodele init i start, care ar fost apelate automat de a s ctre browser. a Facem fereastra vizibil. a

Listing 14.8: Applet i aplicatie s


import java . applet . Applet ; import java . awt .*; public class AppletAplicatie extends Applet public void init () { add ( new Label ( " Applet si aplicatie " ) ) ; } public static void main ( String args []) { AppletAplicatie applet = new AppletAplicatie () ; Frame f = new Frame ( " Applet si aplicatie " ) ; f . setSize (200 , 200) ; f . add ( applet , BorderLayout . CENTER ) ; applet . init () ; applet . start () ; f . show () ; } } {

Capitolul 15 Lucrul cu baze de date


15.1
15.1.1

Introducere
Generaliti despre baze de date at

Aplicatiile care folosesc baze de date sunt, general, aplicatii complexe n folosite pentru gestionarea unor informatii de dimensiuni mari ntr-o manier a sigur i ecient. as a

Ce este o baz de date ? a La nivelul cel mai general, o baz de date reprezint o modalitate de stocare a a a unor informatii (date) pe un suport extern, cu posibilitatea regsirii acestora. a Uzual, o baz de date este memorat a a ntr-unul sau mai multe iere. s Modelul clasic de baze de date este cel relational, care datele sunt n memorate tabele. Un tabel reprezint o structur de date format dintr-o n a a a multime de articole, ecare articol avnd denite o serie de atribute - aceste a atribute corespund coloanelor tabelului, timp ce o linie va reprezenta un n articol. Pe lnga tabele, o baz de date mai poate contine: proceduri i a a s functii, utilizatori i grupuri de utilizatori, tipuri de date, obiecte, etc. s Dintre productorii cei mai importanti de baze de date amintim coma paniile Oracle, Sybase, IBM, Informix, Microsoft, etc. ecare furniznd o a serie ntreag de produse i utilitare pentru lucrul cu baze de date. Aceste a s produse sunt general referite prin termenii DBMS (Database Management n System) sau, traducere, SGBD (Sistem de Gestiune a Bazelor de Date). In n acest capitol vom analiza lucrul cu baze de date din perspectiva programrii a 423

424

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

limbajul Java, fr a descrie particulariti ale unei solutii de stocare a n aa at datelor anume. Vom vedea c, folosind Java, putem crea aplicatii care s a a ruleze fr nici o modicare folosind diverse tipuri de baze care au aceeai aa s structur, ducnd felul acesta notiunea de portabilitate i mai departe. a a n s

Crearea unei baze de date Crearea unei baze de date se face uzual folosind aplicatii specializate oferite de productorul tipului respectiv de sistem de gestiune a datelor, dar exist a a i posibilitatea de a crea o baza folosind un script SQL. Acest aspect ne va s preocupa a mai putin, exemplele prezentate presupunnd c baza a fost ns a a creat deja i are o anumit structur specicat. a s a a a

Accesul la baza de date Se face prin intermediul unui driver specic tipului respectiv de SGBD. Acesta este responsabil cu accesul efectiv la datele stocate, ind legatura dintre aplicatie i baza de date. s

Limbajul SQL SQL (Structured Query Language) reprezint un limaj de programare ce a permite interogarea i actualizarea informatiilor din baze de date relationale. s Acesta este standardizat astfel at diverse tipuri de drivere s se comporte nc a identic, oferind astfel o modalitate unitar de lucru cu baze de date. a

15.1.2

JDBC

JDBC (Java Database Connectivity) este o interfat standard SQL de acces a la baze de date. JDBC este constituit dintr-un set de clase i interfete a s

15.2. CONECTAREA LA O BAZA DE DATE

425

scrise Java, furniznd mecanisme standard pentru proiectantii aplicatiilor n a ce folosesc de baze de date. Folosind JDBC este uor s transmitem secvente SQL ctre baze de date s a a relationale. Cu alte cuvinte, nu este necesar s scriem un program pentru a a accesa o baz de date Oracle, alt program pentru a accesa o baz de date a a Sybase i asa mai departe. Este de ajuns s scriem un singur program folosind s a API-ul JDBC i acesta va capabil s comunice cu drivere diferite, trimitnd s a a secvente SQL ctre baza de date dorit. Bine eles, scriind codul surs a a nt a n Java, ne este asigurat portabilitatea programului. Deci, iat dou motive a a a puternice care fac combinatia Java - JDBC demn de luat seam. a n a Pachetele care ofer suport pentru lucrul cu baze de date sunt java.sql a ce reprezint nucleul tehnologiei JDBC i, preluat de pe platforma J2EE, a s javax.sql. In linii mari, API-ul JDBC ofer urmtoarele faciliti: a a at 1. Stabilirea unei conexiuni cu o baz de date. a 2. Efectuarea de secvente SQL. 3. Prelucrarea rezultatelor obtinute.

15.2

Conectarea la o baz de date a

Procesul de conectare la o baz de date implic efectuarea a dou operatii: a a a 1. Inregistrarea unui driver corespunztor. a 2. Realizarea unei conexiuni propriu-zise. Denitie O conexiune (sesiune) la o baz de date reprezint un context prin care a a sunt trimise secvente SQL i primite rezultate. Intr-o aplicatie pot exista s simultan mai multe conexiuni la baze de date diferite sau la aceeai baz. s a Clasele i interfetele responsabile cu realizarea unei conexiuni sunt: s DriverManager - este clasa ce se ocup cu a nregistrarea driverelor ce vor folosite aplicatie; n

426

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

Driver - interfata pe care trebuie s o implementeze orice clas ce a a descrie un driver; DriverPropertyInfo - prin intermediul acestei clase pot specicate diverse proprieti ce vor folosite la realizarea conexiunilor; at Connection - descrie obiectele ce modeleaz o conexiune propriu-zis a a cu baza de date.

15.2.1

Inregistrarea unui driver

Primul lucru pe care trebuie s-l fac o aplicatie procesul de conectare a a n la o baz de date este s a a nregistreze la maina virtual ce ruleaz aplicatia s a a driverul JDBC responsabil cu comunicarea cu respectiva baz de date. Acest a lucru presupune arcarea memorie a clasei ce implementeaz driver-ul i nc n a s poate realizat mai multe modaliti. a n at a. Folosirea clasei DriverManager: DriverManager.registerDriver(new TipDriver()); b. Folosirea metodei Class.forName ce apeleaz ClassLoader-ul mainii a s virtuale: Class.forName("TipDriver"); Class.forName("TipDriver").newInstance(); c. Setarea proprietii sistem jdbc.drivers, care poate realizat dou at a n a feluri: De la linia de comand: a java -Djdbc.drivers=TipDriver Aplicatie Din program: System.setProperty("jdbc.drivers", "TipDriver"); Folosind aceast metod, specicarea mai multor drivere se face separnd a a a numele claselor cu punct i virgul. s a Dac sunt a nregistrate mai multe drivere, ordinea de precedent alegerea a n driverului folosit la crearea unei noi conexiuni este: 1) Driverele nregistrate folosind proprietatea jdbc.drivers la initializarea mainii virtuale ce va rula procesul. s 2) Driverele nregistrate dinamic din aplicatie.

15.2. CONECTAREA LA O BAZA DE DATE

427

15.2.2

Specicarea unei baze de date

O dat ce un driver JDBC a fost a nregistrat, acesta poate folosit la stabilirea unei conexiuni cu o baz de date. Avnd vedere faptul ca pot exista mai a a n multe drivere arcate memorie, trebuie s avem posibilitea de a specica nc n a pe lng un identicator al bazei de date i driverul ce trebuie folosit. Aceasta a a s se realizeaz prin intermediul unei adrese specice, numit JDBC URL, ce a a are urmtorul format: a jdbc:sub-protocol:identicator Cmpul sub-protocol denumete tipul de driver ce trebuie folosit pentru a s realizarea conexiunii i poate odbc, oracle, sybase, db2 i aa mai departe. s s s Identicatorul bazei de date este un indicator specic ecrui driver corea spunztor bazei de date cu care aplicatia dorete s interactioneze. In functie a s a de tipul driver-ului acest identicator poate include numele unei maini s gazd, un numr de port, numele unui ier sau al unui director, etc., ca a a s exemplele de mai jos: n jdbc:odbc:test jdbc:oracle:thin@persistentjava.com:1521:test jdbc:sybase:test jdbc:db2:test Subprotocolul odbc este un caz specical, sensul c permite specicarea n a cadrul URL-ului a unor atribute ce vor realizate la crearea unei conexin uni. Sintaxa completa subprotocolului odbc este: jdbc:odbc:identicator[;atribut=valoare]* jdbc:odbc:test jdbc:odbc:test;CacheSize=20;ExtensionCase=LOWER jdbc:odbc:test;UID=duke;PWD=java La primirea unui JDBC URL, DriverManager-ul va parcurge lista driverelor nregistrate memorie, pna cnd unul dintre ele va recunoate URL-ul ren a a s spectiv. Dac nu exista nici unul potrivit, atunci va lansata o exceptie de a tipul SQLException, cu mesajul "no suitable driver".

428

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

15.2.3

Tipuri de drivere

Tipurile de drivere existente ce pot folosite pentru realizarea unei conexiuni prin intermediul JDBC se mpart urmtoarele categorii: n a

Tip 1. JDBC-ODBC Bridge

Acest tip de driver permite conectarea la o baz de date care a fost a nregistrat prealabil ODBC. ODBC (Open Database Conectivity) a n n reprezint o modalitate de a uniformiza accesul la baze de date, asociind a acestora un identicator DSN (Data Source Name) i diveri parametri neces s sari conectrii. Conectarea efectiv la baza de date se va face prin intermediul a a acestui identicator, driver-ul ODBC efectund comunicarea cu driverul naa tiv al bazei de date. Dei simplu de utilizat, solutia JDBC-ODBC nu este portabil i comunis as carea cu baza de date sufer la nivelul vitezei de executie datorit multiplelor a a redirectri a ntre drivere. De asemenea, att ODBC-ul ct i driver-ul nativ a a s trebuie s existe pe maina pe care ruleaz aplicatia. a s a Clasa Java care descrie acest tip de driver JDBC este: sun.jdbc.odbc.JdbcOdbcDriver i este inclus distributia standard J2SDK. Specicarea bazei de date se s a n face printr-un URL de forma: jdbc:odbc:identicator unde identif icator este prolul (DSN) creat bazei de date ODBC. n

Tip 2. Driver JDBC - Driver nativ

15.2. CONECTAREA LA O BAZA DE DATE

429

Acest tip de driver transform cererile JDBC direct apeluri ctre a n a driverul nativ al bazei de date, care trebuie instalat prealabil. Clase Java n care implementeaz astfel de drivere pot procurate de la productorii de a a SGBD-uri, distributia standard J2SDK neincluznd nici unul. a

Tip 3. Driver JDBC - Server

Acest tip de driver transform cererile JDBC folosind un protocol de retea a independent, acestea ind apoi transormate folosind o aplicatie server ntr-un protocol specc bazei de date. Introducerea serverului ca nivel intermediar aduce exibilitate maxim sensul c vor putea realizate conexiuni cu a n a diferite tipuri de baze, fr nici o modicare la nivelul clientului. Protocolul aa folosit este specic ecrui productor. a a

Tip 4. Driver JDBC nativ

430

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

Acest tip de driver transform cererile JDBC direct cereri ctre baza a n a de date folosind protocolul de retea al acesteia. Aceast solutie este cea mai a rapid, ind preferat la dezvoltarea aplicatiilor care manevreaz volume a a a mari de date i viteza de executie este critic. Drivere de acest tip pot s a procurate de la diveri productori de SGBD-uri. s a

15.2.4

Realizarea unei conexiuni

Metoda folosit pentru realizarea unei conexiuni este getConnection din a clasa DriverManager i poate avea mai multe forme: s Connection c = DriverManager.getConnection(url); Connection c = DriverManager.getConnection(url, username, password); Connection c = DriverManager.getConnection(url, dbproperties); Stabilirea unei conexiuni folosind driverul JDBC-ODBC String url = "jdbc:odbc:test" ; // sau url = "jdbc:odbc:test;UID=duke;PWD=java" ; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(ClassNotFoundException e) { System.err.print("ClassNotFoundException: " + e) ; return ; } Connection con ; try { con = DriverManager.getConnection(url, "duke", "java"); } catch(SQLException e) { System.err.println("SQLException: " + e);

15.3. EFECTUAREA DE SECVENTE SQL } finally { try{ con.close ; } catch(SQLException e) { System.err.println(SQLException: " + e) ; } }

431

Stabilirea unei conexiuni folosind un driver MySql Folosirea diferitelor tipuri de drivere implic doar schimbarea numelui clasei a ce reprezint driverul i a modalitii de specicare a bazei de date. a s at String url = "jdbc:mysql://localhost/test" ; // sau url = "jdbc:mysql://localhost/test?user=duke&password=java"; try { Class.forName("com.mysql.jdbc.Driver") ; } catch(ClassNotFoundException e) { ... O conexiune va folosit pentru: a Crearea de secvente SQL utilizate pentru interogarea sau actualizarea bazei. Aarea unor informatii legate de baza de date (meta-date). De asemenea, clasa Connection asigur faciliti pentru controlul tranzactiilor a at din memorie ctre baza de date prin metodele commit, rollback, setAua toCommit. Inchiderea unei conexiuni se realizeaz prin metoda close. a

15.3

Efectuarea de secvente SQL

O dat facut conectarea cu metoda DriverManager.getConection, se poate a a folosi obiectul Connection rezultat pentru a se crea obiecte de tip Statement,PreparedStatement sau CallableStatement cu ajutorul crora a putem trimite secvente SQL ctre baza de date. Cele mai uzuale comenzi a SQL sunt cele folosite pentru: Interogarea bazei de date: SELECT

432

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

Actualizarea datelor: INSERT, UPDATE, DELETE Actualizarea structurii: CREATE, ALTER, DROP - acestea mai sunt numite instructiuni DDL (Data Denition Language) Apelarea unei proceduri stocate: CALL Dup cum vom vedea, obtinerea i prelucrarea rezultatelor unei interogri a s a este realizat prin intermediul obiectelor de tip ResultSet. a

15.3.1

Interfata Statement

Interfata Statement ofer metodele de baz pentru trimiterea de secvente a a SQL ctre baza de date i obtinerea rezultatelor, celelalte dou interfete: a s a PreparedStatement i CallableStatement ind derivate din aceasta. s Crearea unui obiect Statement se realizeaz prin intermediul metodei a createStatement a clasei Connection, fr nici un argument: aa Connection con = DriverManager.getConnection(url); Statement stmt = con.createStatement(); Executia unei secvente SQL poate realizat prin intermediul a trei a metode:

1. executeQuery Este folosit pentru realizarea de interogri de tip SELECT. Metoda returneaz a a a un obiect de tip ResultSet ce va contine sub o form tabelar rezultatul a a interogrii. a String sql = "SELECT * FROM persoane"; ResultSet rs = stmt.executeQuery(sql);

2. executeUpdate Este folosit pentru actualizarea datelor (INSERT, UPDATE, DELETE) sau a a structurii bazei de date (CREATE, ALTER, DROP). Metoda va returna un ntreg ce semnic numrul de linii afectate de operatiunea de actualizare a a a datelor, sau 0 cazul unei instructiuni DDL. n

15.3. EFECTUAREA DE SECVENTE SQL String sql = "DELETE FROM persoane WHERE cod > 100"; int linii = stmt.executeUpdate(sql); // Nr de articole care au fost afectate (sterse) sql = "DROP TABLE temp"; stmt.executeUpdate(sql); // returneaza 0

433

3. execute Aceast metod va folosit doar dac este posibil ca rezultatul unei ina a a a terogri s e format din dou sau mai multe obiecte de tip ResultSet sau a a a rezultatul unei actualizri s e format din mai mule valori, sau o combinatie a a ntre aceste cazuri. Aceast situatie, dei mai rar, este posibil atunci cnd a s a a a sunt executate proceduri stocate sau secvente SQL cunoscute abia la momen tul executiei, programatorul netiind deci dac va vorba de o actualizare s a a datelor sau a structurii. Metoda ntoarce true dac rezultatul obtinut a este format din obiecte de tip ResultSet i false dac e format din s a ntregi. In functie de aceasta, pot apelate metodele: getResultSet sau getUp dateCount pentru a aa efectiv rezultatul comenzii SQL. Pentru a prelua toate rezultatele va apelat metoda getMoreResults, dup care vor a a apelate din nou metodele amintite, pn la obtinerea valorii null, respectiv a a 1. Secventa complet de tratare a metodei execute este prezentat mai a a jos: String sql = "comanda SQL necunoscuta"; stmt.execute(sql); while(true) { int rowCount = stmt.getUpdateCount(); if(rowCount > 0) { // Este o actualizare datelor System.out.println("Linii afectate = " + rowCount); stmt.getMoreResults(); continue; } if(rowCount = 0) { // Comanda DDL sau nici o linie afectata System.out.println("Comanda DDL sau 0 actualizari");

434

CAPITOLUL 15. LUCRUL CU BAZE DE DATE stmt.getMoreResults(); continue; } // rowCount este -1 // Avem unul sau mai multe ResultSet-uri ResultSet rs = stmt.getResultSet(); if(rs != null) { // Proceseaza rezultatul ... stmt.getMoreResults(); continue; } // Nu mai avem nici un rezultat break;

} Folosind clasa Statement, cazul care dorim s introducem valorile n n a unor variabile ntr-o secvent SQL, nu avem alt solutie dect s crem un a a a a a ir de caractere compus din instructiuni SQL i valorile variabilelor: s s int cod = 100; String nume = "Popescu"; String sql = "SELECT * FROM persoane WHERE cod=" + cod + " OR nume=" + nume + ""; ResultSet rs = stmt.executeQuery(sql);

15.3.2

Interfata PreparedStatement

Interfata PreparedStatement este derivat din Statement, ind diferit de a a aceasta urmtoarele privinte: n a Instantele de tip PreparedStatement contin secvente SQL care au fost deja compilate (sunt pregtite). a O secvent SQL specicat unui obiect PreparedStatement poate s a a a aib unul sau mai multi parametri de intrare, care vor specicati a prin intermediul unui semn de ntrebare (?) locul ecruia dintre n a

15.3. EFECTUAREA DE SECVENTE SQL

435

ei. Inainte ca secventa SQL s poat executat ecrui parametru a a a a de intrare trebuie s i se atribuie o valoare, folosind metode specice a acestei clase. Executia repetat a aceleiai secvente SQL, dar cu parametri diferiti, va a s general mai rapid dac folosim PreparedStatement, deoarece nu mai n a a trebuie s crem cte un obiect de tip Statement pentru ecare apel SQL, ci a a a refolosim o singur instant precompilat furnizndu-i doar alte argumente. a a a a Crearea unui obiect de tip PreparedStatement se realizeaz prin intera mediul metodei prepareStatement a clasei Connection, specicn ca ara gument o secvent SQL ce contine cte un semn de a a ntrebare pentru ecare parametru de intrare: Connection con = DriverManager.getConnection(url); String sql = "UPDATE persoane SET nume=? WHERE cod=?"; Statement pstmt = con.prepareStatement(sql); Obiectul va pstmt contine o comand SQL precompilat care este trimis a a a imediat ctre baza de date, unde va atepta parametri de intrare pentru a a s putea executat. a Trimiterea parametrilor se realizeaz prin metode de tip setXXX, a unde XXX este tipul corespunztor parametrului, iar argumentele metodei a sunt numrul de ordine al parametrului de intrare (al semnului de a ntrebare) i valoarea pe care dorim s o atribuim. s a pstmt.setString(1, "Ionescu"); pstmt.setInt(2, 100); Dup stabilirea parametrilor de intrare secventa SQL poate executat. a a Putem apoi stabili alte valori de intrare i refolosi obiectul PreparedStatement s pentru executii repetate ale comenzii SQL. Este a posibil ca SGBD-ul ns folosit s nu suporte acest tip de operatiune i s nu retin obiectul prea s a a compilat pentru executii ulterioare. In aceast situatie folosirea interfetei a PreparedStatement loc de Statement nu va n mbunti nici un fel a at n performanta codului, din punctul de vedere al vitezei de executie a acestuia. Executia unei secvente SQL folosind un obiect PreparedStatement se realizeaz printr-una din metodele executeQuery, executeUpdate sau a execute, semnicatiile lor ind aceleai ca i cazul obiectelor de tip s s n Statement, cu singura deosebire c cazul de fat ele nu au nici un ara n a gument.

436

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

String sql = "UPDATE persoane SET nume=? WHERE cod=?"; Statement pstmt = con.prepareStatement(sql); pstmt.setString(1, "Ionescu"); pstmt.setInt(2, 100); pstmt.executeUpdate(); pstmt.setString(1, "Popescu"); pstmt.setInt(2, 200); pstmt.executeUpdate(); sql = "SELECT * from persoane WHERE cod >= ?"; pstmt = con.prepareStatement(sql); pstmt.setInt(1, 100); ResultSet rs = pstmt.executeQuery(); Fiecrui tip Java corespunde un tip generic SQL. Este responsabilitatea a i programatorului s se asigure c folosete metoda adecvat de tip setXXX la a a s a stabilirea valorii unui parametru de intrare. Lista tuturor tipurilor generice disponibile, numite i tipuri JDBC, este denit de clasa Types, prin cons a stantelor declarate de aceasta. Metoda setObject permite specicarea unor valori pentru parametrii de intrare, atunci cnd dorim s folosim maparea a a implicit a ntre tipurile Java i cele JDBC sau atunci cnd dorim s precizm s a a a explicit un tip JDBC. pstmt.setObject(1, "Ionescu", Types.CHAR); pstmt.setObject(2, 100, Types.INTEGER); // sau doar pstmt.setObject(2, 100); Folosind metoda setNull putem s atribuim unui parametru de intrare a valoare SQL NULL, trebuind a s specicm i tipul de date al coloanei ns a a s n care vom scrie aceast valoare. Acelai lucru poate realizat cu metode de a s tipul setXXX dac argumentul folosit are valoarea null. a pstmt.setNull(1, Types.CHAR); pstmt.setInt(2, null); Cu ajutorul metodelor setBytes sau setString avem posibilitatea de a specica date de orice dimensiuni ca valori pentru anumite articole din baza de date. Exist a situatii cnd este de preferat ca datele de mari dimensia ns a uni s e transferate pe buci de o anumit dimensiune. Pentru a realiza a at a

15.3. EFECTUAREA DE SECVENTE SQL

437

acest lucru API-ul JDBC pune la dispozitie metodele setBinaryStream, setAsciiStream i setUnicodeStream care ataeaz un ux de intrare pe s s a octeti, caractere ASCII, respectiv UNICODE, unui parametru de intrare. Pe msur ce sunt citite date de pe ux, ele vor atribuite parametrului. Exema a plul de mai jos ilustreaz acest lucru, atribuind coloanei continut continutul a unui anumit ier: s File file = new File("date.txt"); int fileLength = file.length(); InputStream fin = new FileInputStream(file); java.sql.PreparedStatement pstmt = con.prepareStatement( "UPDATE fisiere SET continut = ? WHERE nume = date.txt"); pstmt.setUnicodeStream (1, fin, fileLength); pstmt.executeUpdate(); La executia secventei, uxul de intrare va apelat repetat pentru a furniza datele ce vor scrise coloana continut a articolului specicat. n Observati c este necesar a s tim dinainte dimensiunea datelor ce vor a n a s scrise, acest lucru ind solicitat de unele tipuri de baze de date.

15.3.3

Interfata CallableStatement

Interfata CallableStatement este derivat din PreparedStatement, instantele a de acest tip oferind o modalitate de a apela o procedur stocat a a ntr-o baz a de date, ntr-o manier standar pentru toate SGBD-urile. a Crearea unui obiect CallableStatement se realizeaz prin metoda prea pareCall a clasei Connection: Connection con = DriverManager.getConnection(url); CallableStatement cstmt = con.prepareCall( "{call proceduraStocata(?, ?)}"); Trimiterea parametrilor de intrare se realizeaz a ntocmai ca la PreparedStatement, cu metode de tip setXXX. Dac procedura are i parametri de ieire (valori a s s returnate), acetia vor trebui s nregistrati cu metoda registerOutParame ter nainte de executia procedurii. Obtinerea valorilor rezultate parametrii n de ieie se va face cu metode de tip getXXX. s CallableStatement cstmt = con.prepareCall(

438

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

"{call calculMedie(?)}"); cstmt.registerOutParameter(1, java.sql.Types.FLOAT); cstmt.executeQuery(); float medie = cstmt.getDouble(1); Este posibil ca un parametru de intrare s e i parametru de ieire. In a s s acest caz el trebuie s primeasc o valoare cu setXXX i, de asemenea, va a a s nregistrat cu registerOutParameter, tipurile de date specicate trebuind s coincid. a a

15.3.4 15.3.5

Obtinerea i prelucrarea rezultatelor s Interfata ResultSet

In urma executie unei interogri SQL rezultatul va reprezentat printr-un a obiect de tip ResultSet, ce va contine toate liniile ce satisfac conditiile impuse de comanda SQL. Forma general a unui ResultSet este tabelar, a a avnd un numr de coloane i de linii, functie de secventa executat. De a a s a asemenea, obiectul va contine i meta-datele interogrii cum ar denumirele s a coloanelor selectate, numrul lor, etc. a Statement stmt = con.createStatement(); String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); Rezultatul interogrii de mai sus va obiectul rs cu urmtoarea structur: a a a cod nume 100 Ionescu 200 Popescu Pentru a extrage informatiile din aceast structur va trebui s parcurgem a a a tabelul linie cu linie i din ecare s extragem valorile de pe coloane. Pentru s a acest lucru vom folosi metode de tip getXXX, unde XXX este tipul de dat al unei coloane iar argumentul primit indic e numrul de ordine din a a a cadrul tabelului, e numele acestuia. Coloanele sunt numerotate de la stnga a la dreapta, ncepnd cu 1. In general, folosirea indexului coloanei loc de a n numele su va mai ecient. De asemenea, pentru maxim portabilitate a a a se recomand citirea coloanelor ordine de la stnga la dreapta i ecare a n a s citire s se fac o singur dat. a a a a

15.3. EFECTUAREA DE SECVENTE SQL

439

Un obiect ResultSet folosete un cursor pentru a parcurge articolele s rezultate urma unei interogri. Initial acest cursor este pozitionat n a naintea primei linii, ecare apel al metodei next determinnd trecerea la urmtoarea a a linie. Deoarece next returneaz false cnd nu mai sunt linii de adus, uzual a a va folosit o bucl while-loop petru a itera prin articolele tabelului: a a String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { int cod = r.getInt("cod"); String nume = r.getString("nume"); /* echivalent: int cod = r.getInt(1); String nume = r.getString(2); */ System.out.println(cod + ", " + nume); } Implicit, un tabel de tip ResultSet nu poate modicat iar cursorul asociat nu se deplaseaz dect a a nainte, linie cu linie. Aadar, putem itera s prin rezultatul unei interogri o singur dat i numai de la prima la ultima a a as linie. Este a posibil s crem ResultSet-uri care s permit modicarea ns a a a a sau deplasarea ambele sensuri. Exemplul urmtor va folosi un cursor care n a este modicabil i nu va reecta schimbrile produse de alti utilizatori dup s a a crearea sa: Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); Dac un ResultSet folosete un cursor modicabil i care poate naviga a s s ambele sensuri, atunci are la dispozitie o serie de metode ce se bazeaz pe n a acest suport: absolute - Deplaseaz cursorul la o anumit linie specicat absolut; a a a updateXXX - Actualizeaz valoarea unei coloane din linia curent, a a unde XXX este un tip de date.

440

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

updateRow - Transfer actualizrile fcute liniei baza de date. a a a n moveToInsertRow - deplaseaz cursorul la o linie spceial, numit a a a linie nou, utilizate pentru a introduce noi articole baza de date. a a n Linia curent anterioar a cursorului va memorat pentru a se putea a a a reveni la ea. insertRow - insereaz articolul din zona linie nou baza de date; a a n cursorul trebuie s e pozitionat le linia nou la executia acestei operatiuni. a a moveToCurrentRow - revine la linia curent din tabel. a deleteRow - terge linia curent din tabel i din baza de date; nu s a s poate apelat cnd cursorul este modul linie nou. a a n a Nu toate sistemele de gestiune a bazelor de date ofer suport pentru a folosirea cursoarelor care pot modicate. Pentru a determina dac baza de a date permite acest lucru pot utilizate metodele supportsPositionedUpdate i supportsPositionedDelete ale clasei DatabaseMetaData. In cazul s care acest lucru este permis, este responsabilitatea driver-ului bazei de n date s asigure rezolvarea problemelor legate de actualizarea concurent a a a unui cursor, astfel at s nu apar anomalii. nc a a

15.3.6

Exemplu simplu

In continuare vom da un exemplul simplu de utilizare a claselor de baz a mentionate anterior. Programul va folosi o baz de date MySql, ce contine a un tabel numit persoane, avnd coloanele: cod, nume i salariu. Scriptul a s SQL de creare a bazei este: create table persoane(cod integer, nume char(50), salariu double); Aplicatia va goli tabelul cu persoane, dup care va aduga aleator un a a numr de articole, va efectua aarea lor i calculul mediei salariilor. a s s Listing 15.1: Exemplu simplu de utilzare JDBC
import java . sql .*; public class TestJdbc { public static void main ( String [] args ) {

15.3. EFECTUAREA DE SECVENTE SQL


String url = " jdbc : mysql :// localhost / test " ; try { Class . forName ( " com . mysql . jdbc . Driver " ) ; } catch ( Cl as sNo tF oun dE x c e p t i o n e ) { System . out . println ( " Eroare incarcare driver !\ n " + e ) ; return ; } try { Connection con = DriverManager . getConnection ( url ) ; // Golim tabelul persoane String sql = " DELETE FROM persoane " ; Statement stmt = con . createStatement () ; stmt . executeUpdate ( sql ) ;

441

// Adaugam un numar de persoane generate aleator // Tabelul persoane are coloanele ( cod , nume , salariu ) int n = 10; sql = " INSERT INTO persoane VALUES (? , ? , ?) " ; PreparedStatement pstmt = con . prepareStatement ( sql ) ; for ( int i =0; i < n ; i ++) { int cod = i ; String nume = " Persoana " + i ; double salariu = 100 + Math . round ( Math . random () * 900) ; // salariul va fi intre 100 si 1000 pstmt . setInt (1 , cod ) ; pstmt . setString (2 , nume ) ; pstmt . setDouble (3 , salariu ) ; pstmt . executeUpdate () ; } // Afisam persoanele ordonate dupa salariu sql = " SELECT * FROM persoane ORDER BY salariu " ; ResultSet rs = stmt . executeQuery ( sql ) ; while ( rs . next () ) System . out . println ( rs . getInt ( " cod " ) + " , " + rs . getString ( " nume " ) + " , " + rs . getDouble ( " salariu " ) ) ;

// Calculam salariul mediu sql = " SELECT avg ( salariu ) FROM persoane " ; rs = stmt . executeQuery ( sql ) ; rs . next () ;

442

CAPITOLUL 15. LUCRUL CU BAZE DE DATE


System . out . println ( " Media : " + rs . getDouble (1) ) ; // Inchidem conexiunea con . close () ; } catch ( SQLException e ) { e . printStackTrace () ; }

} }

15.4
15.4.1

Lucrul cu meta-date
Interfata DatabaseMetaData

Dup realizarea unui conexiuni la o baz de date, putem apela metoda geta a MetaData pentru a aa diverse informatii legate de baza respectiv, aa a s numitele meta-date (date despre date); Ca rezult al apelului metodei, vom obtine un obiect de tip DatabaseMetaData ce ofer metode pentru deter a minarea tabelelor, procedurilor stocate, capabilitilor conexiunii, gramaticii at SQL suportate, etc. ale bazei de date. Programul urmtor aeaz numele tuturor tabelelor dintr-o baz de dat a s a a nregistrat ODBC. a n Listing 15.2: Folosirea interfetei DatabaseMetaData
import java . sql .*; public class TestMetaData { public static void main ( String [] args ) { String url = " jdbc : odbc : test " ; try { Class . forName ( " sun . jdbc . odbc . JdbcOdbcDriver " ) ; } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) { System . out . println ( " Eroare incarcare driver !\ n " + e ) ; return ; } try { Connection con = DriverManager . getConnection ( url ) ; DatabaseMetaData dbmd = con . getMetaData () ; ResultSet rs = dbmd . getTables ( null , null , null , null ) ;

15.4. LUCRUL CU META-DATE


while ( rs . next () ) System . out . println ( rs . getString ( " TABLE_NAME " ) ) ; con . close () ; } catch ( SQLException e ) { e . printStackTrace () ; } } }

443

15.4.2

Interfata ResultSetMetaData

Meta-datele unui ResultSet reprezint informatiile despre rezultatul continut a acel obiect cum ar numrul coloanelor, tipul i denumirile lor, etc. Acesn a s tea sunt obtinute apelnd metoda getMetaData pentru ResultSet-ul re a spectiv, care va returna un obiect de tip ResultSetMetaData ce poate apoi folosit pentru extragerea informatiilor dorite. ResultSet rs = stmt.executeQuery("SELECT * FROM tabel"); ResultSetMetaData rsmd = rs.getMetaData(); // Aflam numarul de coloane int n = rsmd.getColumnCount(); // Aflam numele coloanelor Sring nume[] = new String[n+1]; for(int i=1; i<=n; i++) nume[i] = rsmd.getColumnName(i);

444

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

Capitolul 16 Lucrul dinamic cu clase


16.1 Incrcarea claselor memorie a n

Dup cum tim executia unei aplicatii Java este realizat de ctre maina a s a a s virtual Java (JVM), aceasta ind responsabil cu interpretarea codului de a a octeti rezultat urma compilrii. Spre deosebire de alte limbaje de progra n a mare cum ar C sau C++, un program Java compilat nu este descris de un ier executabil ci de o multime de iere cu extensia .class corespunztoare s s a ecrei clase a programului. In plus, aceste clase nu sunt arcate toate a n n memorie la pornirea aplicatiei, ci sunt arcate pe parcursul executie acesteia n atunci cnd este nevoie de ele, momentul efectiv care se realizeaz acest a n a lucru depinznd de implementarea mainii virtuale. a s Ciclul de viat al unei clase are aadar urmtoarele etape: a s a 1. Incrcarea - Este procesul regsirii reprezentrii binare a unei clase a a a (ierul .class) pe baza numelui complet al acestuia i arcarea acess s nc teia memorie. In urma acestui proces, va instantiat un obiect de n tip java.lang.Class, corespunztor clasei respective. Operatiunea de a arcare a unei clase este realizat la un moment ce precede prima nc a utilizare efectiv a sa. a 2. Editarea de legturi - Specic incorporarea noului tip de date JVM a a n pentru a putea utlizat. 3. Initializarea - Const executia blocurilor statice de initializare i a n s initializarea variabilelor de clas. a 445

446

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

4. Descrcarea - Atunci cnd nu mai exist nici o referint de tipul clasei a a a a respective, obiectul de tip Class creat va marcat pentru a eliminat din memorie de ctre garbage collector. a Incrcarea claselor unei aplicatii Java memorie este realizat prin ina n a termediul unor obiecte pe care le vom numi generic class loader. Acestea sunt de dou tipuri: a 1. Class loader-ul primordial (eng. bootstrap) - Reprezint o parte intea grant a mainii virtuale, ind responsabil cu arcarea claselor stana s nc dard din distributia Java. 2. Class loader-e proprii - Acestea nu fac parte intrinsec din JVM i a s sunt instante ale clasei java.lang.ClassLoader. Aceasta este o clas a abstract, tipul efectiv al obiectului ind aadar derivat din aceasta. a s Dup cum vom vedea, la executia unui program Java vor create implicit a dou obiecte de tip ClassLoader pentru arcarea memorei a claselor a nc n proprii ale aplicatiei. Exist a posibilitarea de a crea noi tipuri derivate din a ns ClassLoader specializate pentru arcarea claselor conform unor specicatii nc anume care s realizeze diverse optimizri. Astfel, arcarea unei clase poate a a nc determina arcarea unor altor clase care sigur vor folosite nc mpreun cu a prima, sau a unor resurse ce sunt necesare functionrii acesteia, etc. a

Incepnd cu versiunea 1.2 de Java, a fost introdus un model de tip delegat, a care class loader-ele sunt dispuse ierarhic n ntr-un arbore, rdcina acestuia a a ind class loader-ul primordial. Fiecare instanta de tip ClassLoader va avea aadar un printe (evident, mai putin rdcina), acesta ind specicat la s a a a crearea sa. In momentul cnd este solicitat arcarea unei clase, un classa a nc loader poate delega primul rnd operatiunea de arcare printelui su n a nc a a care va delega la rndul su cererea mai departe pn la class loader-ul a a a a primordial sau pn unul din acetia reuete s o a a s s s a ncarce. Abia cazul n n care nici unul din acetia nu a reuit, va s s ncerca s execute operatiunea de a arcare a clasei. Dac nici ea nu va reui, va aruncat o exceptie de tipul nc a s a ClassNotFoundException. Dei acest comportament nu este obligatoriu, s multe situatii el este de preferat, pentru a minimiza arcarea aceleiai n nc s clase de mai multe ori, folosind class loader-e diferite.

16.1. INCARCAREA CLASELOR MEMORIE IN

447

Implicit, Java 2 JVM ofer trei class loader-e, unul primordial i dou a s a proprii, cunoscute sub numele de: Boostrap Class Loader - Class loader-ul primordial. Acesta este responsabil cu arcarea claselor din distributia Java standard (cele din nc pachetele java.*, javax.*, etc.). Extension Class Loader - Utilizat pentru arcarea claselor din direcnc toarele extensiilor JRE. System Class Loader - Acesta este responsabil cu arcarea claselor nc proprii aplicatiilor Java (cele din CLASSPATH). Tipul acestuia este java.lang.URLClassLoader.

Intruct tipurile de date Java pot arcate folosind diverse instante a nc de tip ClassLoader, ecare obiect Class va retine class loader-ul care a fost folosit pentru arcare, acesta putnd obtinut cu metoda getClassnc a Loader.

Incrcarea dinamic a unei clase memorie se refer la faptul c nu a a n a a cunoastem tipul acesteia dect la executia preogramului, moment care a n putem solicita arcarea sa, specicnd numele su complet prin intermediul nc a a unui ir de caractere. Acest lucru poate realizat prin mai multe modaliti, s at cele mai comune metode ind:

448

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

loadClass apelat pentru un obiect de tip ClassLoader a

ClassLoader loader = new MyClassLoader(); loader.loadClass("NumeCompletClasa"); Class.forName Aceast metoda va arca respectiva clas folosind class loader-ul a nc a obiectului curent (care o apeleaz): a Class c = Class.forName("NumeCompletClasa"); // echivalent cu ClassLoader loader = this.getClass().getClassLoader(); loader.loadClass("ClasaNecunoscuta"); // Clasele standard pot fi si ele incarcate astfel Class t = Class.forName("java.lang.Thread"); Dac dorim s instantiem un obiect dintr-o clas arcat dinamic putem a a a nc a folosi metoda newInstance, cu conditia s existe constructorul fr argu a aa mente pentru clasa respectiv. Dup cum vom vedea sectiunea urmtoare, a a n a mai exist i alte posibiliti de a instantia astfel de obiecte. as at Class c = Class.forName("java.awt.Button"); Button b = (Button) c.newInstance();

Folosirea interfetelor sau a claselor abstracte mpreun cu arcarea dia nc namic a claselor ofer un mecanism extrem de puternic de lucru Java. a a n Vom detalia acest lucru prin intermediul unui exepmplu. S presupunem a c dorim s crem o aplicatie care s genereze aleator un vector de numere a a a a dup care s aplice o anumit functie acestui vector. Numele functiei care a a a trebuie apelat va introdus de la tastatur, iar implementarea ei va a a continut a ntr-o clas a directorului curent. Toate functiile vor extinde clasa a abstract Functie. In felul acesta, aplicatia poate extins cu noi functii a a fr a schimba codul ei, tot ce trebuie s facem ind s scriem noi clase care aa a a extind Functie i s implementm metoda executa. Aceasta va returna 0 s a a dac metoda s-a executat corect, 1 caz contrar. a n

16.1. INCARCAREA CLASELOR MEMORIE IN Listing 16.1: Exemplu de arcare dinamic a claselor nc a
import java . util .*; import java . io .*; public class TestFunctii {

449

public static void main ( String args []) throws IOException { // Generam un vector aleator int n = 10; int v [] = new int [ n ]; Random rand = new Random () ; for ( int i =0; i < n ; i ++) v [ i ] = rand . nextInt (100) ; // Citim numele unei functii BufferedReader stdin = new BufferedReader ( new InputStreamReader ( System . in ) ) ; String numeFunctie = " " ; while (! numeFunctie . equals ( " gata " ) ) { System . out . print ( " \ nFunctie : " ) ; numeFunctie = stdin . readLine () ; try { // Incarcam clasa Class c = Class . forName ( numeFunctie ) ; // Cream un obiect de tip Functie Functie f = ( Functie ) c . newInstance () ; // Setam vectorul f . setVector ( v ) ; // sau f . v = v ; // Executam functia int ret = f . executa () ; System . out . println ( " \ nCod returnat : " + ret ) ; } catch ( Cl as sNo tF oun d E x c e p t i o n e ) { System . err . println ( " Functie inexistenta ! " ) ; } catch ( In st ant ia tio n E x c e p t i o n e ) { System . err . println ( " Functia nu poate fi instantiata ! "); } catch ( Il le gal Ac ces s E x c e p t i o n e ) { System . err . println ( " Functia nu poate fi accesata ! " ) ;

450
} } } }

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

Listing 16.2: Clasa abstract ce descrie functia a


public abstract class Functie { public int v [] = null ; public void setVector ( int [] v ) { this . v = v ; } public abstract int executa () ; }

Listing 16.3: Un exemplu de functie


import java . util .*; public class Sort extends Functie { public int executa () { if ( v == null ) return -1; Arrays . sort ( v ) ; for ( int i =0; i < v . length ; i ++) System . out . print ( v [ i ] + " " ) ; return 0; } }

Listing 16.4: Alt exemplu de functie


public class Max extends Functie { public int executa () { if ( v == null ) return -1; int max = v [0]; for ( int i =1; i < v . length ; i ++) if ( max < v [ i ]) max = v [ i ]; System . out . print ( max ) ; return 0;

16.1. INCARCAREA CLASELOR MEMORIE IN


} }

451

Un obiect de tip URLClassLoader mentine o list de adrese URL de unde a va ncerca s a ncarce memorie clasa al crei nume specicm ca argun a l a ment al metodelor de mai sus. Implicit, la crearea class loader-ului aceast a list este completat cu informatiile din variabila sistem CLASSPATH sau cu a a cele specicate prin optiunea -classpath la lansarea aplicatiei. Folosind metoda getURLs putem aa aceste adrese, iar cu addURL putem aduga a o nou adres de cutare a claselor. Bine eles, adresele URL pot specica a a a nt i directoare ale sistemului de iere local. S presupunem c directorul s s a a n c:\clase\demo exist clasa cu numele Test, aat pachetul demo i dorim a a n s s o arcm dinamic memorie: a nc a n // Obtinem class loaderul curent URLClassLoader urlLoader = (URLClassLoader) this.getClass().getClassLoader(); // Adaugam directorul sub forma unui URL urlLoader.addURL(new File("c:\\clase").toURL()); // Incarcam clasa urlLoader.loadClass("demo.Test"); Dup ce o clas a fost arcat folosind un class loader, ea nu va mai a a nc a putea descrcat explicit din memorie. In cazul care dorim s avem posia a n a bilitatea de a o re arca, deoarece a fost modicat i recompilat, trebuie s nc as a a folosim class-loadere proprii i s instantiem noi obiecte de tip ClassLoader, s a ori de cte ori dorim s fortm re arcarea claselor. Crearea unui class a a a nc loader propriu se face uzual prin extinderea clasei URLClassLoader, o variant simplist ind prezentat mai jos: a a a public class MyClassLoader extends URLClassLoader{ public MyClassLoader(URL[] urls){ super(urls); } } Incrcarea claselor folosind clasa nou creat se va face astfel: a a

452

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

// La initializare URLClassLoader systemLoader = (URLClassLoader) this.getClass().getClassLoader(); URL[] urls = systemLoader.getURLs(); // Cream class loaderul propriu MyClassLoader myLoader = new MyClassLoader(urls); myLoader.loadClass("Clasa"); ... // Dorim sa reincarcam clasa myLoader.loadClass("Clasa"); // nu functioneaza ! // Cream alt class loader MyClassLoader myLoader = new MyClassLoader(urls); myLoader.loadClass("Clasa"); // reincarca clasa

16.2

Mecanismul reectrii a

Mecanismul prin care o clas, interfat sau obiect reect la momentul a a a executiei structura lor intern se numete reectare (eng. reection), acesta a s punnd la dispozitie metode pentru: a Determinarea clasei unui obiect. Aarea unor informatii despre o clas (modicatori, superclasa, con a structori, metode). Instantierea unor clase al cror nume este tiut abia la executie. a s Setarea sau aarea atributelor unui obiect, chiar dac numele acestora a este tiut abia la executie. s Invocarea metodelor unui obiect al cror nume este tiut abia la executie. a s Crearea unor vectori a cror dimensiune i tip nu este tiut dect la a s s a executie. Suportul pentru reectare este inclus distributia standard Java, ind n cunoscut sub numele de Reection API i contine urmtoarele clase: s a

16.2. MECANISMUL REFLECTARII java.lang.Class java.lang.Object Clasele din pachetul java.lang.reect i anume: s Array Constructor Field Method Modifier

453

16.2.1

Examinarea claselor i interfetelor s

Examinarea claselor i interfetelor se realizeaz cu metode ale clasei java.lang.Class, s a un obiect de acest tip putnd s reprezinte att o clas ct i o interfat, a a a a a s a diferentierea acestora fcndu-se prin intermediul metodei isInterface. a a Reection API pune la dispozitie metode pentru obtinerea urmtoarelor a informatii:

Aarea instantei Class corespunztor unui anumit obiect sau tip de a date: Class c = obiect.getClass(); Class c = java.awt.Button.class; Class c = Class.forName("NumeClasa"); Tipurile primitive sunt descrise i ele de instante de tip Class avnd forma s a TipPrimitiv.class: int.class, double.class, etc., diferentierea lor fcndu a a se cu ajutorul metodei isPrimitive.

Aarea numelui unei clase - Se realizeaz cu metoda getName: a Class clasa = obiect.getClass(); String nume = clasa.getName();

454

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

Aarea modicatorilor unei clase - Se realizeaz cu metoda getModifiers, a aceasta returnnd un numr a a ntreg ce codic toti modicatorii clasei. Pena tru a determina uor prezenta unui anumit modicator se folosesc metodele s statice ale clasei Modifier isPublic, isAbstract i isFinal: s Class clasa = obiect.getClass(); int m = clasa.getModifiers(); String modif = ""; if (Modifier.isPublic(m)) modif += "public "; if (Modifier.isAbstract(m)) modif += "abstract "; if (Modifier.isFinal(m)) modif += "final "; System.out.println(modif + "class" + c.getName());

Aarea superclasei - Se realizeaz cu metoda getSuperclass ce rea turneaz o instant de tip Class, corespunztoare tipului de date al supera a a clasei sau null pentru clasa Object. Class c = java.awt.Frame.class; Class s = c.getSuperclass(); System.out.println(s); // java.awt.Window Class c = java.awt.Object.class; Class s = c.getSuperclass(); // null

Aarea interfetelor implementate de o clas sau extinse de o interfat a a - Se realizeaz cu metoda getInterfaces, ce returneaz un vector de tip a a Class[]. public void interfete(Class c) { Class[] interf = c.getInterfaces(); for (int i = 0; i < interf.length; i++) { String nume = interf[i].getName(); System.out.print(nume + " ");

16.2. MECANISMUL REFLECTARII } } ... interfete(java.util.HashSet.class); // Va afisa interfetele implementate de HashSet: // Cloneable, Collection, Serializable, Set

455

interfete(java.util.Set); // Va afisa interfetele extinse de Set: // Collection

Aarea variabilelor membre - Se realizeaz cu una din metodele a getFields sau getDeclaredFields, ce returnez un vector de tip Field[], a diferenta ntre cele dou constnd faptul c prima returneaz toate varia a n a a abilele membre, inclusiv cele motenite, timp ce a doua le returnez doar s n a pe cele declarate cadrul clasei. La rndul ei, clasa Field pune la dispozitie n a metodele getName, getType i getModifiers pentru a obtine numele, tipul, s respectiv modicatorii unei variabile membru. Cu ajutorul metodei getField este posibil obtinerea unei referinte la o a variabil mebr cu un anumit nume specicat. a a

Aarea constructorilor - Se realizeaz cu metodele getConstructors a sau getDeclaredConstructors, ce returneaz un vector de tip Constructor[]. a Clasa Constructor pune la dispozitie metodele getName, getParameterTypes, getModifiers, getExceptionTypes pentru a obtine toate informatiile legate de respectivul constructor. Cu ajutorul metodei getConstructor este posibil obtinerea unei referinte a la constructor cu o signatur specicat. a a

Aarea metodelor - Se realizeaz cu metodele getMethods sau a getDeclaredMethods, ce returneaz un vector de tip Method[]. Clasa Method a pune la dispozitie metodele getName, getParameterTypes, getModifiers, getExceptionTypes, getReturnType pentru a obtine toate informatiile legate

456

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

de respectiva metod. a Cu ajutorul metodei getMethod este posibil obtinerea unei referinte la a o metod cu o signatur specicat. a a a

Aarea claselor imbricate - Se realizeaz cu metodele getClasses sau a getDeclaredClasses, ce returnez un vector de tip Class[]. a

Aarea clasei de acoperire - Se realizeaz cu metoda getDeclaringClass. a Aceast metod o regsim i clasele Field, Constructor, Method, pentru a a a s n acestea returnnd clasa crei apartine variabila, constructorul sau metoda a a i respectiv. a

16.2.2

Manipularea obiectelor

Pe lng posibilitatea de a examina structura unei anumite clase la momentul a a executiei, folosind Reection API avem posibilitatea de a lucra dinamic cu obiecte, bazndu-ne pe informatii pe care le obtinem abia la executie. a

Crearea obiectelor Dup cum stim, crearea obiectelor se realizeaz cu operatorul new urmat de a a un apel la un constructor al clasei pe care o instantiem. In cazul care n numele clasei nu este cunoscut dect la momentul executiei nu mai putem a folosi aceast metod de instantiere. In schimb, avem la dispozitie alte dou a a a variante: Metoda newInstance din clasa java.lang.Class Aceasta permite instantierea unui obiect folosind constructorul fr ar aa gumente al acestuia. Dac nu exist un astfel de constructor sau nu este a a accesibil vor generate exceptii de tipul InstantiationException, re spectiv IllegalAccessException. Class c = Class.forName("NumeClasa"); Object o = c.newInstance(); // Daca stim tipul obiectului

16.2. MECANISMUL REFLECTARII Class c = java.awt.Point.class; Point p = (Point) c.newInstance();

457

Metoda newInstance din clasa Constructor Aceasta permite instantierea unui obiect folosind un anumit construc tor, cel pentru care se face apelul. Evident, aceast solutie presupune a primul rnd obtinerea unui obiect de tip Constructor cu o anumit n a a signatur, apoi specicarea argumentelor la apelarea sa. S rescriem a a exemplul de mai sus, apelnd constructorul cu dou argumente al clasei a a Point. Class clasa = java.awt.Point.class; // Obtinem constructorul dorit Class[] signatura = new Class[] {int.class, int.class}; Constructor ctor = clasa.getConstructor(signatura); // Pregatim argumentele // Ele trebuie sa fie de tipul referinta corespunzator Integer x = new Integer(10); Integer y = new Integer(20); Object[] arg = new Object[] {x, y}; // Instantiem Point p = (Point) ctor.newInstance(arg); Exceptii generate de metoda newInstance sunt: InstantiationException, IllegalAccessException, IllegalArgumentException i s InvocationTargetException. Metoda getConstructor poate provoca exceptii de tipul NoSuchMethodException.

Invocarea metodelor Invocarea unei metode al crei nume cunoatem abia la momentul executiei a l s se realizeaz cu metoda invoke a clasei Method. Ca i cazul construca s n torilor, trebuie s obtinem ai o referint la metoda cu signatura corea nt a spunztoare i apoi s specicm argumentele. In plus, mai putem obtine a s a a valoarea returnat. S presupunem c dorim s apelm metoda contains a a a a a

458

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

a clasei Rectangle care determin dac un anumit punct se gsete intea a a s n riorul drepunghiului. Metoda contains are mai multe variante, noi o vom apela pe cea care accept un argument de tip Point. a Class clasa = java.awt.Rectangle.class; Rectangle obiect = new Rectangle(0, 0, 100, 100); // Obtinem metoda dorita Class[] signatura = new Class[] {Point.class}; Method metoda = clasa.getMethod("contains", signatura); // Pregatim argumentele Point p = new Point(10, 20); Object[] arg = new Object[] {p}; // Apelam metoda metoda.invoke(obiect, arg); Dac numrul argumentelor metodei este 0, atunci putem folosi vala a oarea null locul vectorilor ce reprezint signatura, respectiv parametri n a de apelare ai metodei. Exceptiile generate de metoda invoke sunt: IllegalAccessException i InvocationTargetException. Metoda getMethod poate provoca exceptii s de tipul NoSuchMethodException.

Setarea i aarea variabilelor membre s Pentur setarea i aarea valorilor variabilelor membre sunt folosite metodele s set i get ale clasei Field. S presupunem c dorim s setm variabila x a s a a a a unui obiect de tip Point i s obtinem valoarea variabilei y a aceluiai obiect: s a s Class clasa = java.awt.Point.class; Point obiect = new Point(0, 20); // Obtinem variabilele membre Field x, y; x = clasa.getField("x"); y = clasa.getField("y");

16.2. MECANISMUL REFLECTARII // Setam valoarea lui x x.set(obiect, new Integer(10)); // Obtinem valoarea lui y Integer val = y.get(obiect);

459

Exceptiile generate de metodele get, set sunt: IllegalAccessException i IllegalArgumentException. Metoda getField poate provoca exceptii de s tipul NoSuchFieldException.

Revenind la exemplul din sectiunea anterioar cu apelarea dinamic a a a unor functii pentru un vector, s presupunem c exist deja un numr a a a a nsemnat de clase care descriu diferite functii dar acestea nu extind clasa abstract a Functie. Din acest motiv, solutia anterioar nu mai este viabil i trebuie a as s folosim apelarea metodei executa a ntr-un mod dinamic. Listing 16.5: Lucru dinamic cu metode i variabile s
import java . lang . reflect .*; import java . util .*; import java . io .*; public class TestFunctii2 { public static void main ( String args []) throws IOException { // Generam un vector aleator int n = 10; int v [] = new int [ n ]; Random rand = new Random () ; for ( int i =0; i < n ; i ++) v [ i ] = rand . nextInt (100) ; // Citim numele unei functii BufferedReader stdin = new BufferedReader ( new InputStreamReader ( System . in ) ) ; String numeFunctie = " " ; while (! numeFunctie . equals ( " gata " ) ) { System . out . print ( " \ nFunctie : " ) ; numeFunctie = stdin . readLine () ;

460

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE


try { // Incarcam clasa Class c = Class . forName ( numeFunctie ) ; // Cream un obiect de tip Functie Object f = c . newInstance () ; // Setam vectorul ( setam direct variabila v ) Field vector = c . getField ( " v " ) ; vector . set (f , v ) ; // Apelam metoda executa // Folosim null pentru ca nu avem argumente Method m = c . getMethod ( " executa " , null ) ; Integer ret = ( Integer ) m . invoke (f , null ) ; System . out . println ( " \ nCod returnat : " + ret ) ; } catch ( Exception e ) { System . err . println ( " Eroare la apelarea functiei ! " ) ; } }

} }

16.2.3

Lucrul dinamic cu vectori

Vectorii sunt reprezentati ca tip de date tot prin intermediul clasei java.lang.Class, diferentierea fcndu-se prin intermediul metodei isArray. a a Tipul de date al elementelor din care este format vectorul va obtinut cu ajutorul metodei getComponentType, ce ntoarce o referint de tip Class. a Point []vector = new Point[10]; Class c = vector.getClass(); System.out.println(c.getComponentType()); // Va afisa: class java.awt.Point Lucrul dinamic cu obiecte ce reprezint vectori se realizeaz prin intera a mediul clasei Array. Aceasta contine o serie de metode statice ce permit: Crearea de noi vectori: newInstance Aarea numrului de elemente: getLength a

16.2. MECANISMUL REFLECTARII Setarea / aarea elementelor: set, get

461

Exemplul de mai jos creeaz un vector ce contine numerele a ntregi de la 0 la 9: Object a = Array.newInstance(int.class, 10); for (int i=0; i < Array.getLength(a); i++) Array.set(a, i, new Integer(i)); for (int i=0; i < Array.getLength(a); i++) System.out.print(Array.get(a, i) + " ");

You might also like