Sunteți pe pagina 1din 462

Curs practic de Java

Cristian Fr asinaru

Cuprins
1 Introducere n Java 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 si interpretat 1.2 Primul program . . . . . . . . . . . . . . . . . 1.3 Structura lexical a a limbajului Java . . . . . . 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 si variabile . . . . . . . . . . . 1.4.1 Tipuri de date . . . . . . . . . . . . . . 1.4.2 Variabile . . . . . . . . . . . . . . . . . 1.5 Controlul execut iei . . . . . . . . . . . . . . . 1.5.1 Instruct iuni de decizie . . . . . . . . . 1.5.2 Instruct iuni de salt . . . . . . . . . . . 1.5.3 Instruct iuni pentru tratarea except iilor 1.5.4 Alte instruct iuni . . . . . . . . . . . . 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 a si eterogeni 1.7 S iruri de caractere . . . . . . . . . . . . . . . . . . 1.8 Folosirea argumentelor de la linia de comand a . . . 1.8.1 Transmiterea argumentelor . . . . . . . . . . 1.8.2 Primirea argumentelor . . . . . . . . . . . . 1.8.3 Argumente numerice . . . . . . . . . . . . . 2 Obiecte si clase 2.1 Ciclul de viat a al unui obiect . . . . . . . . . . . . . 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 si super . . . . . . . . . . . . . . . . . . 2.3 Implementarea metodelor . . . . . . . . . . . . . . 2.3.1 Declararea metodelor . . . . . . . . . . . . . 2.3.2 Tipul returnat de o metod a . . . . . . . . . 2.3.3 Trimiterea parametrilor c atre o metod a . . . 2.3.4 Metode cu num ar variabil de argumente . . 2.3.5 Supra nc arcarea si supradenirea metodelor 2.4 Modicatori de acces . . . . . . . . . . . . . . . . . 2.5 Membri de instant a si membri de clas a . . . . . . . 2.5.1 Variabile de instant a si de clas a . . . . . . . 2.5.2 Metode de instant a si de clas a . . . . . . . . 2.5.3 Utilitatea membrilor de clas a . . . . . . . . 2.5.4 Blocuri statice de init ializare . . . . . . . . . 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 si metode abstracte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

CUPRINS 2.7.1 Declararea unei clase abstracte 2.7.2 Metode abstracte . . . . . . . . 2.8 Clasa Object . . . . . . . . . . . . . . 2.8.1 Orice clas a are o superclas 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 Except ii 3.1 Ce sunt except iile ? . . . . . . . . . . . . 3.2 Prinderea si tratarea except iilor . . . . 3.3 Aruncarea except iilor . . . . . . . . . . 3.4 Avantajele trat arii except iilor . . . . . . 3.4.1 Separarea codului pentru tratarea 3.4.2 Propagarea erorilor . . . . . . . . 3.4.3 Gruparea erorilor dup a tipul lor . 3.5 Ierarhia claselor ce descriu except ii . . . 3.6 Except ii la execut ie . . . . . . . . . . . . 3.7 Crearea propriilor except ii . . . . . . . .

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

4 Intr ari si ie siri 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 siere . . . . . . . . . . 4.2.5 Citirea si scrierea cu buer . . . . . . . . . . . . 4.2.6 Concatenarea uxurilor . . . . . . . . . . . . . . 4.2.7 Fluxuri pentru ltrarea datelor . . . . . . . . . 4.2.8 Clasele DataInputStream si DataOutputStream 4.3 Intr ari si ie siri formatate . . . . . . . . . . . . . . . . . 4.3.1 Intr ari formatate . . . . . . . . . . . . . . . . . 4.3.2 Ie siri formatate . . . . . . . . . . . . . . . . . .

4 4.4

CUPRINS Fluxuri standard de intrare si ie sire . . . . . . . . . . . . . 4.4.1 Asarea informat iilor pe ecran . . . . . . . . . . . . 4.4.2 Citirea datelor de la tastatur a . . . . . . . . . . . . 4.4.3 Redirectarea uxurilor standard . . . . . . . . . . . 4.4.4 Analiza lexical a pe uxuri (clasa StreamTokenizer) 4.5 Clasa RandomAccesFile ( siere cu acces direct) . . . . . . 4.6 Clasa File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 112 112 113 115 117 119

5 Interfet e 5.1 Introducere . . . . . . . . . . . . . . . . . . 5.1.1 Ce este o interfat a? . . . . . . . . . 5.2 Folosirea interfet elor . . . . . . . . . . . . . 5.2.1 Denirea unei interfet e . . . . . . . . 5.2.2 Implementarea unei interfet e . . . . . 5.2.3 Exemplu: implementarea unei stive . 5.3 Interfet e si clase abstracte . . . . . . . . . . 5.4 Mo stenire multipl a prin interfet e . . . . . . 5.5 Utilitatea interfet elor . . . . . . . . . . . . . 5.5.1 Crearea grupurilor de constante . . . 5.5.2 Transmiterea metodelor ca parametri 5.6 Interfat a FilenameFilter . . . . . . . . . . 5.6.1 Folosirea claselor anonime . . . . . . 5.7 Compararea obiectelor . . . . . . . . . . . . 5.7.1 Interfat a Comparable . . . . . . . . . 5.7.2 Interfat a 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 interfet e . 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 sierelor . . . . . . . . . . . 6.2.1 Organizarea sierelor surs 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 unit a tilor de compilare (.class) 6.2.3 Necesitatea organiz arii sierelor . . . . . . . . 6.2.4 Setarea c aii de c autare (CLASSPATH) . . . . 6.3 Arhive JAR . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Folosirea utilitarului jar . . . . . . . . . . . . 6.3.2 Executarea aplicat iilor arhivate . . . . . . . . 7 Colect ii 7.1 Introducere . . . . . . . . . . 7.2 Interfet e ce descriu colect ii . . 7.3 Implement ari ale colect iilor . . 7.4 Folosirea ecient a a colect iilor 7.5 Algoritmi polimorci . . . . . 7.6 Tipuri generice . . . . . . . . 7.7 Iteratori si enumer ari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 154 155 156 157 158 159

161 . 161 . 162 . 166 . 168 . 170 . 171 . 172 177 . 177 . 179 . 180 . 180 . 181 . 183 . 183 . 184 . 187 . 188 . 193 . 194 . 196 199 . 199 . 200 . 202 . 204 . 206 . 207

8 Serializarea obiectelor 8.1 Folosirea serializ arii . . . . . . . . . . . . . . . 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 interfet ei Serializable . 8.2.2 Controlul serializ arii . . . . . . . . . . 8.3 Personalizarea serializ arii obiectelor . . . . . . 8.3.1 Controlul versiunilor claselor . . . . . . 8.3.2 Securizarea datelor . . . . . . . . . . . 8.3.3 Implementarea interfet ei Externalizable 8.4 Clonarea obiectelor . . . . . . . . . . . . . . . 9 Interfat a grac a cu utilizatorul 9.1 Introducere . . . . . . . . . . . . . . . . . . . 9.2 Modelul AWT . . . . . . . . . . . . . . . . . . 9.2.1 Componentele AWT . . . . . . . . . . 9.2.2 Suprafet e de a sare (Clasa Container) 9.3 Gestionarea pozit ion arii . . . . . . . . . . . . 9.3.1 Folosirea gestionarilor de pozit ionare .

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 si a claselor anonime . 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 Suprafet e de desenare - clasa Canvas 10.2 Contextul grac de desenare . . . . . . . . . 10.2.1 Propriet a tile contextului grac . . . .

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 A sarea imaginilor . . . . . . . . . 10.5.2 Monitorizarea nc arc arii imaginilor 10.5.3 Mecanismul de double-buering . 10.5.4 Salvarea desenelor n format JPEG 10.5.5 Crearea imaginilor n memorie . . Tip arirea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7 275 276 277 279 282 286 287 289 291 291 292 293

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 Asem an ari si deosebiri cu AWT . . 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 Considerat ii generale . . . . . . . . 11.8 Look and Feel . . . . . . . . . . . . . . . .

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

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

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

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

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

299 . 299 . 299 . 300 . 301 . 304 . 305 . 307 . 310 . 310 . 314 . 316 . 316 . 316 . 319 . 324 . 329 . 332 . 335 . 336 . 336 . 338 . 340

8 12 Fire de execut ie 12.1 Introducere . . . . . . . . . . . . . . . . . 12.2 Crearea unui r de execut ie . . . . . . . . 12.2.1 Extinderea clasei Thread . . . . . . 12.2.2 Implementarea interfet ei Runnable 12.3 Ciclul de viat a al unui r de execut ie . . . 12.3.1 Terminarea unui r de execut ie . . 12.3.2 Fire de execut ie de tip daemon . 12.3.3 Stabilirea priorit a tilor de execut ie . 12.3.4 Sincronizarea relor de execut ie . . 12.3.5 Scenariul produc ator / consumator 12.3.6 Monitoare . . . . . . . . . . . . . . 12.3.7 Semafoare . . . . . . . . . . . . . . 12.3.8 Probleme legate de sincronizare . . 12.4 Gruparea relor de execut ie . . . . . . . . 12.5 Comunicarea prin uxuri de tip pipe . . 12.6 Clasele Timer si TimerTask . . . . . . . .

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 n ret ea 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 c atre mai mult i client i 14 Appleturi 14.1 Introducere . . . . . . . . . . . . . . . 14.2 Crearea unui applet simplu . . . . . . . 14.3 Ciclul de viat a al unui applet . . . . . 14.4 Interfat a grac a cu utilizatorul . . . . . 14.5 Denirea si folosirea parametrilor . . . 14.6 Tag-ul APPLET . . . . . . . . . . . . 14.7 Folosirea relor de execut ie n appleturi 14.8 Alte metode oferite de clasa Applet . . 14.9 Arhivarea appleturilor . . . . . . . . . 14.10Restrict ii de securitate . . . . . . . . . 14.11Appleturi care sunt si aplicat ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS 15 Lucrul cu baze de date 15.1 Introducere . . . . . . . . . . . . . . . . . . 15.1.1 Generalit a ti despre baze de date . . . 15.1.2 JDBC . . . . . . . . . . . . . . . . . 15.2 Conectarea la o baz a de date . . . . . . . . . 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 secvent e SQL . . . . . . . . . 15.3.1 Interfat a Statement . . . . . . . . . . 15.3.2 Interfat a PreparedStatement . . . . . 15.3.3 Interfat a CallableStatement . . . . . 15.3.4 Obt inerea si prelucrarea rezultatelor 15.3.5 Interfat a ResultSet . . . . . . . . . . 15.3.6 Exemplu simplu . . . . . . . . . . . . 15.4 Lucrul cu meta-date . . . . . . . . . . . . . 15.4.1 Interfat a DatabaseMetaData . . . . . 15.4.2 Interfat a ResultSetMetaData . . . . 16 Lucrul dinamic cu clase 16.1 Inc arcarea claselor n memorie . . . . . . 16.2 Mecanismul reect arii . . . . . . . . . . 16.2.1 Examinarea claselor si interfet elor 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 n Java


1.1 Ce este Java ?

Java este o tehnologie inovatoare lansat a de compania Sun Microsystems n 1995, care a avut un impact remarcabil asupra ntregii comunit a ti a dezvoltatorilor de software, impun andu-se prin calit a ti deosebite cum ar simplitate, robustet e si nu n ultimul r and portabilitate. Denumit a init ial OAK, tehnologia Java este format a dintr-un limbaj de programare de nivel nalt pe baza c aruia sunt construite o serie de platforme destinate implement arii de aplicat ii pentru toate segmentele industriei software.

1.1.1

Limbajul de programare Java

Inainte de a prezenta n detaliu aspectele tehnice ale limbajului Java, s a amintim caracteristicile sale principale, care l-au transformat ntr-un interval de timp at at de scurt ntr-una din cele mai pupulare opt iuni pentru dezvoltarea de aplicat ii, indiferent de domeniu sau de complexitatea lor. Simplitate - elimin a supra nc arcarea operatorilor, mo stenirea multipl a si toate facilit a tile ce pot provoca scrierea unui cod confuz. U surint a n crearea de aplicat ii complexe ce folosesc programarea n ret ea, re de execut ie, interfat a grac a, baze de date, etc. Robustet e - elimin a sursele frecvente de erori ce apar n programare prin renunt area la pointeri, administrarea automat a a memoriei si elim11

12

CAPITOLUL 1. INTRODUCERE IN JAVA inarea pierderilor de memorie printr-o procedur a de colectare a obiectelor care nu mai sunt referite, ce ruleaz a n fundal (garbage collector). Complet orientat pe obiecte - elimin a complet stilul de programare procedural. Securitate - este un limbaj de programare foarte sigur, furniz and mecanisme stricte de securitate a programelor concretizate prin: vericarea dinamic a a codului pentru detectarea secvent elor periculoase, impunerea unor reguli stricte pentru rularea proceselor la distant a, etc. Neutralitate arhitectural a - comportamentul unei aplicat ii Java nu depinde de arhitectura zic a a ma sinii pe care ruleaz a. Portabililtate - Java este un limbaj independent de platforma de lucru, aceea si aplicat ie rul and f ar a nici o modicare si f ar a a necesita recompilarea ei pe sisteme de operare diferite cum ar Windows, Linux, Mac OS, Solaris, etc. lucru care aduce economii substant iale rmelor dezvoltatoare de aplicat ii. Este compilat si interpretat, aceasta ind solut ia ecient a pentru obt inerea portabilit a tii. Performant a - de si mai lent dec at limbajele de programare care genereaz a executabile native pentru o anumit a platform a de lucru, compilatorul Java asigur a o performant a ridicat a a codului de octet i, astfel nc at viteza de lucru put in mai sc azut a nu va un impediment n dezvoltarea de aplicat ii oric at de complexe, inclusiv grac a 3D, animat ie, etc. Este modelat dup a C si C++, trecerea de la C, C++ la Java f ac andu-se foarte u sor.

1.1.2

Platforme de lucru Java

Limbajul de programare Java a fost folosit la dezvoltarea unor tehnologii dedicate rezolv arii unor probleme din cele mai diverse domenii. Aceste tehnologii au fost grupate n a sa numitele platforme de lucru, ce reprezint a seturi de libr arii scrise n limbajul Java, precum si diverse programe utilitare, folosite pentru dezvoltarea de aplicat ii sau componente destinate unei anume categorii de utilizatori.

1.1. CE ESTE JAVA ?

13

J2SE (Standard Edition) Este platforma standard de lucru ce ofer a suport pentru crearea de aplicat ii independente si appleturi. De asemenea, aici este inclus a si tehnologia Java Web Start ce furnizeaz a o modalitate extrem de facil a pentru lansarea si instalarea local a a programelor scrise n Java direct de pe Web, oferind cea mai comod a solut ie pentru distribut ia si actualizarea aplicat iilor Java. 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 a platform a ofer a API-ul necesar dezvolt arii de aplicat ii complexe, formate din componente ce trebuie s a ruleze n sisteme eterogene, cu informat iile memorate n baze de date distribuite, etc. Tot aici g asim si suportul necesar pentru crearea de aplicat ii si servicii Web, bazate pe componente cum ar servleturi, pagini JSP, etc. Toate distribut iile Java sunt oferite gratuit si pot desc arcate de pe Internet de la adresa http://java.sun.com. In continuare, vom folosi termenul J2SDK pentru a ne referi la distribut ia standard J2SE 1.5 SDK (Tiger).

1.1.3

Java: un limbaj compilat si interpretat

In funct ie de modul de execut ie a aplicat iilor, limbajele de programare se mpart n dou a categorii: Interpretate: instruct iunile sunt citite linie cu linie de un program numit interpretor si traduse n instruct iuni ma sin a. Avantajul acestei solut ii este simplitatea si faptul c a ind interpretat a direct sursa programului obt inem portabilitatea. Dezavantajul evident este viteza de execut ie redus a. Probabil cel mai cunoscute limbaj interpretat este limbajul Basic. Compilate: codul surs a al programelor este transformat de compilator ntr-un cod ce poate executat direct de procesor, numit cod

14

CAPITOLUL 1. INTRODUCERE IN JAVA ma sin a. Avantajul este execut ia extrem de rapid a, dezavantajul ind lipsa portabilit a tii, codul compilat ntr-un format de nivel sc azut nu poate rulat dec at pe platforma de lucru pe care a fost compilat.

Limbajul Java combin a solut iile amintite mai sus, programele Java ind at at interpretate c at si compilate. A sadar vom avea la dispozit ie un compilator responsabil cu transformarea surselor programului n a sa numitul cod de octet i, precum si un interpretor ce va executa respectivul cod de octet i. Codul de octet i este diferit de codul ma sin a. Codul ma sin a este reprezentat de o succesiune de instruct iuni specice unui anumit procesor si unei anumite platforme de lucru reprezentate n format binar astfel nc at s a poat a executate f ar a a mai necesita nici o prelucrare. Codurile de octet i sunt seturi de instruct iuni care seam an a cu codul scris n limbaj de asamblare si sunt generate de compilator independent de mediul de lucru. In timp ce codul ma sin a este executat direct de c atre procesor si poate folosit numai pe platforma pe care a fost creat, codul de octet i este interpretat de mediul Java si de aceea poate rulat pe orice platform a pe care este instalat a mediul de execut ie Java. Prin ma sina virtual a Java (JVM) vom nt elege mediul de execut ie al aplicat iilor Java. Pentru ca un cod de octet i s a poat a executat pe un anumit calculator, pe acesta trebuie s a e instalat a o ma sin a virtual a Java. Acest lucru este realizat automat de c atre distribut ia J2SDK.

1.2

Primul program

Crearea oric arei aplicat ii Java presupune efectuarea urm atorilor pa si:

1. Scriererea codului surs a

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

1.2. PRIMUL PROGRAM

15

Toate aplicat iile Java cont in o clas a principal a(primar a) n care trebuie s a se gaseasc a metoda main. Clasele aplicat iei se pot gasi e ntr-un singur sier, e n mai multe.

2. Salvarea sierelor surs a Se va face n siere care au obligatoriu extensia java, nici o alt a extensie neind acceptat a. Este recomandat ca sierul care cont ine codul surs a al clasei primare s a aib a acela si nume cu cel al clasei, de si acest lucru nu este obligatoriu. S a presupunem c a am salvat exemplul de mai sus n sierul C:\intro\FirstApp.java.

3. Compilarea aplicat iei Pentru compilare vom folosi compilatorul javac din distribut ia J2SDK. Apelul compilatorului se face pentru sierul ce cont ine clasa principal a a aplicat iei sau pentru orice sier/ siere cu extensia java. Compilatorul creeaz a c ate un sier separat pentru ecare clas a a programului. Acestea au extensia .class si implicit sunt plasate n acela si director cu sierele surs a. javac FirstApp.java In cazul n care compilarea a reu sit va generat sierul FirstApp.class.

4. Rularea aplicat iei Se face cu interpretorul java, apelat pentru unitatea de compilare corespunz atoare clasei principale. Deoarece interpretorul are ca argument de intrare numele clasei principale si nu numele unui sier, ne vom pozit iona n directorul ce cont ine sierul FirstApp.class si vom apela interpretorul astfel: java FirstApp Rularea unei aplicat ii care nu folose ste interfat a grac a, se va face ntr-o fereastr a sistem.

16

CAPITOLUL 1. INTRODUCERE IN JAVA

Atent ie Un apel de genul java c:\intro\FirstApp.class este gre sit!

1.3
1.3.1

Structura lexical a a limbajului Java


Setul de caractere

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

1.3.2

Cuvinte cheie

Cuvintele rezervate n Java sunt, cu c ateva except ii, cele din C++ si au fost enumerate n tabelul de mai jos. Acestea nu pot folosite ca nume de clase,

A LIMBAJULUI JAVA 1.3. STRUCTURA LEXICALA

17

interfet e, variabile sau metode. true, false, null nu sunt cuvinte cheie, dar nu pot nici ele folosite ca nume n aplicat ii. Cuvintele marcate prin 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

Incep and cu versiunea 1.5, mai exist a si cuv antul cheie enum.

1.3.3

Identicatori

Sunt secvent e nelimitate de litere si cifre Unicode, ncep and cu o liter a. Dup a cum am mai spus, identicatorii nu au voie s a e identici cu cuvintele rezervate.

1.3.4

Literali

Literalii pot de urm atoarele tipuri: Intregi Sunt acceptate 3 baze de numerat ie : baza 10, baza 16 ( ncep cu caracterele 0x) si baza 8 ( ncep cu cifra 0) si pot de dou a tipuri: normali - se reprezint a pe 4 octet i (32 bit i) lungi - se reprezint a pe 8 octet i (64 bit i) si se termin a cu caracterul L (sau l).

18

CAPITOLUL 1. INTRODUCERE IN JAVA Flotant i Pentru ca un literal s a e considerat otant el trebuie s a aib a cel put in o zecimal a dup a virgul a, s a e n notat ie exponent ial a sau s a aib a suxul F sau f pentru valorile normale - reprezentate pe 32 bit i, respectiv D sau d pentru valorile duble - reprezentate pe 64 bit i. Exemple: 1.0, 2e2, 3f, 4D. Logici Sunt reprezentat i de true - valoarea logic a de adev ar, respectiv false - valoarea logic a de fals.

Atent ie Spre deosebire de C++, literalii ntregi 1 si 0 nu mai au semnicat ia de adev arat, respectiv fals.

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

A LIMBAJULUI JAVA 1.3. STRUCTURA LEXICALA

19

S iruri de caractere Un literal sir de caractere este format din zero sau mai multe caractere ntre ghilimele. Caracterele care formeaz a sirul pot caractere grace sau secvent e escape. Dac a sirul este prea lung el poate scris ca o concatenare de sub siruri de dimensiune mai mic a, concatenarea sirurilor realiz andu-se cu operatorul +, ca n exemplul: "Ana " + " are " + " mere ". Sirul vid este "". Dup a cum vom vedea, orice sir este de fapt o instant a a clasei String, denit a n pachetul java.lang.

1.3.5

Separatori

Un separator este un caracter care indic a sf ar situl unei unit a ti lexicale si nceputul alteia. In Java separatorii sunt urm atorii: ( ) [ ] ; , . . Instruct iunile unui program se separ a cu punct si virgul a.

1.3.6

Operatori

Operatorii Java sunt, cu mici deosebiri, cei din C++: atribuirea: = operatori matematici: +, -, *, /, %, ++, -- . Este permis a notat ia prescurtat a de forma lval op= rval: x += 2 n -= 3 Exist a operatori pentru autoincrementare si autodecrementare (post si pre): x++, ++x, n--, --n Evaluarea expresiilor logice se face prin metoda scurtcircuitului: evaluarea se opre ste n momentul n care valoarea de adev ar a expresiei este sigur determinat a. operatori logici: &&(and), ||(or), !(not) operatori relat ionali: <, <=, >, <=, ==, != operatori pe bit i: &(and), |(or), ^ (xor), ~ (not) operatori de translat ie: <<, >>, >>> (shift la dreapta f ar a semn)

20

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

operatorul , (virgul a) folosit pentru evaluarea secvent ial a a operat iilor: int x=0, y=1, z=2; operatorul + pentru concatenarea sirurilor: 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 a trei feluri de comentarii: Comentarii pe mai multe linii, nchise ntre /* si */. Comentarii pe mai multe linii care tin de documentat ie, nchise ntre /** si */. Textul dintre cele dou a secvent e este automat mutat n documentat ia aplicat iei de c atre generatorul automat de documentat ie javadoc. Comentarii pe o singur a linie, care incep cu //. Observat ii: Nu putem scrie comentarii n interiorul altor comentarii. Nu putem introduce comentarii n interiorul literalilor caracter sau sir de caractere. Secvent ele /* si */ pot s a apar a pe o linie dup a secvent a // dar si pierd semnicat ia. La fel se ntampl a cu secvent a // n comentarii care incep cu /* sau */.

1.4. TIPURI DE DATE S I VARIABILE

21

1.4
1.4.1

Tipuri de date si variabile


Tipuri de date

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

Vectorii, clasele si interfet ele sunt tipuri referint a. Valoarea unei variabile de acest tip este, spre deosebire de tipurile primitive, o referint a (adres a de memorie) c atre valoarea sau mult imea de valori reprezentat a de variabila respectiv a.

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

22

CAPITOLUL 1. INTRODUCERE IN JAVA

1.4.2

Variabile

Variabilele pot de tip primitiv sau referint e la obiecte (tip referint a). Indiferent de tipul lor, pentru a putea folosite variabilele trebuie declarate si, eventual, init ializate. Declararea variabilelor: Tip numeVariabila; Init ializarea variabilelor: Tip numeVariabila = valoare; Declararea constantelor: final Tip numeVariabila; Evident, exist a posibilitatea de a declara si init ializa mai multe variabile sau constante de acela si tip ntr-o singur a instruct iune astfel: Tip variabila1[=valoare1], variabila2[=valoare2],...; Convent ia de numire a variabilelor n Java include, printre altele, urm atoarele criterii: variabilele nale (constante) se scriu cu majuscule; variabilele care nu sunt constante se scriu astfel: prima liter a mic a iar dac a numele variabilei este format din mai mult i atomi lexicali, atunci primele litere ale celorlalt i 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 funct ie de locul n care sunt declarate variabilele se mpart n urm atoatele categorii: a. Variabile membre, declarate n interiorul unei clase, vizibile pentru toate metodele clasei respective c at si pentru alte clase n funct ie de nivelul lor de acces (vezi Declararea variabilelor membre).

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

23

c. Variabile locale, declarate ntr-o metod a, vizibile doar n metoda respectiv a. d. Variabile locale, declarate ntr-un bloc de cod, vizibile doar n blocul respectiv. e. Parametrii de la tratarea except iilor (vezi Tratarea except iilor). 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, r am an locale corpului ciclului: for(int i=0; i<100; i++) { //domeniul de vizibilitate al lui i } i = 101;//incorect Nu este permis a ascunderea unei variabile:

24

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

1.5

Controlul execut iei

Instruct iunile Java pentru controlul execut iei sunt foarte asem an atoare celor din limbajul C si pot mp art ite n urm atoarele categorii: Instruct iuni de decizie: if-else, switch-case Instruct iuni de salt: for, while, do-while Instruct iuni pentru tratarea except iilor: try-catch-finally, throw Alte instruct iuni: break, continue, return, label:

1.5.1
if-else

Instruct iuni de decizie

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

1.5. CONTROLUL EXECUT IEI ... break; ... default: ... }

25

Variabilele care pot testate folosind instruct iunea switch nu pot dec at de tipuri primitive.

1.5.2
for

Instruct iuni de salt

for(initializare; expresie-logica; pas-iteratie) { //Corpul buclei } for(int i=0, j=100 ; i < 100 && j > 0; i++, j--) { ... } At at la init ializare c at si n pasul de iterat ie pot mai multe instruct iuni desp art ite prin virgul a.

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

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

26

CAPITOLUL 1. INTRODUCERE IN JAVA

1.5.3

Instruct iuni pentru tratarea except iilor

Instruct iunile pentru tratarea except iilor sunt try-catch-finally, respectiv throw si vor tratate n capitolul Except ii.

1.5.4

Alte instruct iuni

break: p ar ase ste fort at corpul unei structuri repetitive. continue: termina fort at iterat ia curent a a unui ciclu si trece la urm atoarea iterat ie. return [valoare]: termin a o metod a si, eventual, returneaz a o valorare. numeEticheta: : Dene ste o etichet a. De si n Java nu exist a goto, se pot deni totu si etichete folosite n expresii de genul: break numeEticheata sau continue numeEticheta, utile pentru a controla punctul de ie sire dintr-o structur a repetitiv a, ca 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 urm atoarelor etape:

1.6. VECTORI

27

Declararea vectorului - Pentru a putea utiliza un vector trebuie, nainte de toate, sa-l declar am. Acest lucru se face prin expresii de forma: Tip[] numeVector; sau Tip numeVector[]; ca n exemplele de mai jos: int[] intregi; String adrese[]; Instant ierea Declararea unui vector nu implic a si alocarea memoriei necesare pentru ret inerea elementelor. Operat iunea de alocare a memoriei, numit a si instant ierea vectorului, se realizeaz a ntotdeauna prin intermediul operatorului new. Instant ierea unui vector se va face printr-o expresie de genul: numeVector = new Tip[nrElemente]; unde nrElemente reprezint a num arul maxim de elemente pe care le poate avea vectorul. In urma instant ierii vor alocat i: nrElemente dimensiune(T ip) octet i necesari memor arii elementelor din vector, unde prin dimensiune(T ip) am notat num arul de octet i pe care se reprezint 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 si instant ierea unui vector pot f acute simultan astfel: Tip[] numeVector = new Tip[nrElemente];

28

CAPITOLUL 1. INTRODUCERE IN JAVA Init ializarea (opt ional) Dup a declararea unui vector, acesta poate init ializat, adic a elementele sale pot primi ni ste valori init iale, evident dac a este cazul pentru a sa ceva. In acest caz instant ierea nu mai trebuie facut a explicit, alocarea memoriei f ac andu-se automat n funct ie de num a rul de elemente cu care se init ializeaz a vectorul. String culori[] = {"Rosu", "Galben", "Verde"}; int []factorial = {1, 1, 2, 6, 24, 120};

Primul indice al unui vector este 0, deci pozit iile unui vector cu n elemente vor cuprinse ntre 0 si n 1. Nu sunt permise construct ii de genul Tip numeVector[nrElemente], alocarea memoriei f ac andu-se doar prin intermediul 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 si instant ierea unei matrici vor realizate astfel: Tip matrice[][] = new Tip[nrLinii][nrColoane]; matrice[i] este linia i a matricii si reprezint a un vector cu nrColoane elemente iar matrice[i][j] este elementul de pe linia i si coloana j .

1.6.3

Dimensiunea unui vector

Cu ajutorul variabilei length se poate aa num arul de elemente al unui 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 nt elege modalitatea de folosire a lui length trebuie ment ionat c a ecare vector este de fapt o instant a a unei clase iar length este o variabil a public a a acelei clase, n care este ret inut num arul maxim de elemente al 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 n exemplele de mai jos. Dup a cum vom vedea, o atribuire de genul b = a are alt a semnicat ie dec at copierea elementelor lui a n b si nu poate folosit 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 si algoritmi care s a simplice proceseul de crearea a unui algoritm, programatorul trebuind s a se concentreze pe aspectele specice problemei abordate. Clasa java.util.Arrays ofer a diverse metode foarte utile n lucrul cu vectori cum ar : sort - sorteaz a ascendent un vector, folosind un algoritm de tip QuickSort 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 - c autarea binar a a unei anumite valori ntr-un vector sortat;

30

CAPITOLUL 1. INTRODUCERE IN JAVA equals - testarea egalit a tii valorilor a doi vectori (au acelea si num ar de elemente si pentru ecare indice valorile corespunz atoare din cei doi vectori sunt egale) ll - atribuie ec arui element din vector o valoare specicat a.

1.6.6

Vectori cu dimensiune variabil a si eterogeni

Implement ari ale vectorilor cu num ar variabil de elemente sunt oferite de clase specializate cum ar Vector sau ArrayList din pachetul java.util. Astfel de obiecte descriu vectori eterogeni, ale c aror elemente au tipul Object, si vor studiat i n capitolul Colect ii.

1.7

S iruri de caractere

In Java, un sir de caractere poate reprezentat printr-un vector format din elemente de tip char, un obiect de tip String sau un obiect de tip StringBuer. Dac a un sir de caractere este constant (nu se dore ste schimbarea cont inutului s a pe parcursul execut iei programului) atunci el va declarat de tipul String, altfel va declarat de tip StringBuffer. Diferent a principal a ntre aceste clase este c a StringBuffer pune la dispozit ie metode pentru modicarea cont inutului sirului, cum ar : append, insert, delete, reverse. Uzual, cea mai folosit a modalitate de a lucra cu siruri este prin intermediul clasei String, care are si unele particularit a ti fat a de restul claselor menite s a simplice c at mai mult folosirea sirurilor de caractere. Clasa StringBuffer va utilizat a predominant n aplicat ii dedicate proces arii textelor cum ar editoarele de texte. Exemple echivalente de declarare a unui sir: String s = "abc"; String s = new String("abc"); char data[] = {a, b, c}; String s = new String(data); Observat i prima variant a de declarare a sirului s din exemplul de mai sus - de altfel, cea mai folosit a - care prezint a o particularitate a clasei String fat a de restul claselor Java referitoare la instant ierea obiectelor sale.

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

1.8
1.8.1

Folosirea argumentelor de la linia de comand a


Transmiterea argumentelor

O aplicat ie Java poate primi oric ate argumente de la linia de comanda n momentul lans arii ei. Aceste argumente sunt utile pentru a permite utilizatorului s a specice diverse opt iuni legate de funct ionarea aplicat iei sau s a furnizeze anumite date init iale programului.

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

32

CAPITOLUL 1. INTRODUCERE IN JAVA

Argumentele de la linia de comand a sunt introduse la lansarea unei aplicat ii, ind specicate dup a numele aplicat iei si separate prin spat iu. De exemplu, s a presupunem c a aplicat ia Sortare ordoneaz a lexicograc (alfabetic) liniile unui sier si prime ste ca argument de intrare numele sierului pe care s a l sorteze. Pentru a ordona sierul "persoane.txt", aplicat ia va lansat a astfel: java Sortare persoane.txt A sadar, formatul general pentru lansarea unei aplicat ii care prime ste argumente de la linia de comand a este: java NumeAplicatie [arg0 arg1 . . . argn] In cazul n care sunt mai multe, argumentele trebuie separate prin spat ii iar dac a unul dintre argumente cont ine spat ii, atunci el trebuie pus ntre ghilimele. Evident, o aplicat ie poate s a nu primeasc a nici un argument sau poate s a ignore argumentele primite de la linia de comand a.

1.8.2

Primirea argumentelor

In momentul lans arii unei aplicat ii interpretorul parcurge linia de comand a cu care a fost lansat a aplicat tia si, n cazul n care exist a, transmite programului argumentele specicate sub forma unui vector de siruri. Acesta este primit de aplicat ie ca parametru al metodei main. Reamintim c a formatul metodei main din clasa principal a este: public static void main (String args[]) Vectorul args primit ca parametru de metoda main va cont ine toate argumentele transmise programului de la linia de comand a. In cazul apelului java Sortare persoane.txt vectorul args va cont ine un singur element pe prima s a pozit ie: args[0]="persoane.txt".

Vectoru args este instant iat cu un num ar de elemente egal cu num arul argumentelor primite de la linia de comand a. A sadar, pentru a aa num arul de argumente primite de program este sucient s a a am dimensiunea vectorului args prin intermediul atributului length:

33 1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA public static void main (String args[]) { int numarArgumente = args.length ; } In cazul n care aplicat ia presupune existent a unor argumente de la linia de comand a, ns a acestea nu au fost transmise programului la lansarea sa, vor ap area except ii (erori) de tipul ArrayIndexOutOfBoundsException. Tratarea acestor except ii este prezentat a n capitolul Except ii. Din acest motiv, este necesar s a test am dac a programul a primit argumentele de la linia de comand a necesare pentru funct ionarea sa si, n caz contrar, s a a seze un mesaj de avertizare sau s a foloseasc a ni ste valori implicite, ca 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 cont ine pe prima pozit ie numele aplicat iei, ntruc at n Java numele aplicat iei este chiar numele clasei principale, adic a a clasei n care se gase ste metoda main. S a consider a n continuare un exemplu simplu n care se dore ste a sarea 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 IN JAVA

Un apel de genul java Afisare Hello Java va produce urm atorul rezultat (aplicat ia a primit 2 argumente): Hello Java Apelul java Afisare "Hello Java" va produce ns a alt rezultat (aplicat ia a primit un singur argument): Hello Java

1.8.3

Argumente numerice

Argumentele de la linia de comand a sunt primite sub forma unui vector de siruri (obiecte de tip String). In cazul n care unele dintre acestea reprezint a valori numerice ele vor trebui convertite din siruri n numere. Acest lucru se realizeaz a cu metode de tipul parseTipNumeric aate n clasa corespunzatoare tipului n care vrem s a facem conversia: Integer, Float, Double, etc. S a consider am, de exemplu, c a aplicat ia Power ridic a un numar real la o putere ntreag a, argumentele ind trimise de la linia de comand a sub forma: java Power "1.5" "2" //ridica 1.5 la puterea 2 Conversia celor dou a argumente n numere se va face astfel: 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 except ii (erori) de tipul NumberFormatException n cazul n care sirul primit ca parametru nu reprezint a un numar de tipul respectiv. Tratarea acestor except ii este prezentat a n capitolul Except ii.

Capitolul 2 Obiecte si clase


2.1
2.1.1

Ciclul de viat a al unui obiect


Crearea obiectelor

In Java, ca n orice limbaj de programare orientat-obiect, crearea obiectelor se realizeaz a prin instant ierea unei clase si implic a urm atoarele lucruri: Declararea Presupune specicarea tipului acelui obiect, cu alte cuvinte specicarea clasei acestuia (vom vedea c a tipul unui obiect poate si o interfat a). NumeClasa numeObiect; Instant ierea Se realizeaz a prin intermediul operatorului new si are ca efect crearea efectiv a a obiectului cu alocarea spat iului de memorie corespunz ator. numeObiect = new NumeClasa(); Init ializarea Se realizeaz a prin intermediul constructorilor clasei respective. Init ializarea este de fapt parte integrant a a procesului de instant iere, n sensul c a imediat dup a alocarea memoriei ca efect al operatorului new este apelat constructorul specicat. Parantezele rotunde de dup a numele clasei indic a faptul c a acolo este de fapt un apel la unul din constructorii clasei si nu simpla specicare a numelui clasei. Mai general, instant ierea si init ializarea apar sub forma: 35

36

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

S a consider am urm atorul exemplu, n care declar am si instant iem dou a obiecte din clasa Rectangle, clas a ce descrie suprafet e grace rectangulare, denite de coordonatele colt ului st anga sus (originea) si l a timea, respectiv n alt imea. Rectangle r1, r2; r1 = new Rectangle(); r2 = new Rectangle(0, 0, 100, 200); In primul caz Rectangle() este un apel c atre constructorul clasei Rectangle care este responsabil cu init ializarea obiectului cu valorile implicite. Dup a cum observ am n al doilea caz, init ializarea se poate face si cu anumit i parametri, cu condit ia s a existe un constructor al clasei respective care s a accepte parametrii respectivi. Fiecare clas a are un set de constructori care se ocup a cu init ializare obiectelor nou create. De exemplu, clasa Rectangle are urm atorii constructori: 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, instant ierea si init ializarea obiectului pot ap area pe aceea si linie (cazul cel mai uzual): Rectangle patrat = new Rectangle(0, 0, 100, 100); Obiecte anonime Este posibil a si crearea unor obiecte anonime care servesc doar pentru init ializarea altor obiecte, caz n care etapa de declarare a referint ei obiectului nu mai este prezent a: Rectangle patrat = new Rectangle(new Point(0,0), new Dimension(100, 100));

AL UNUI OBIECT 2.1. CICLUL DE VIAT A

37

Spat iul de memorie nu este pre-alocat Declararea unui obiect nu implic a sub nici o form a alocarea de spat iu 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 a un obiect creat, el poate folosit n urm atoarele sensuri: aarea unor informat ii despre obiect, schimbarea st arii sale sau executarea unor act iuni. 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 construct ii 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 n conformitate cu drepturile de acces pe care le ofer a variabilele respective celorlalte clase. (vezi Modicatori 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 a c a valorile variabilelor pot modicate indirect prin intermediul metodelor sale. Programarea orientat a obiect descurajeaz a folosirea direct a a variabilelor unui obiect deoarece acesta poate adus n st ari inconsistente (ireale). In schimb, pentru ecare variabil a care descrie starea

38

CAPITOLUL 2. OBIECTE S I CLASE

obiectului trebuie s a existe metode care s a permit a schimbarea/aarea valorilor variabilelor sale. Acestea se numesc metode de accesare, sau metode setter - getter si au numele de forma setVariabila, respectiv getVariabila. 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 a tin a evident a obiectelor create si s a le distrug a n mod explicit atunci c and nu mai este nevoie de ele, cu alte cuvinte s a administreze singur memoria ocupat a de obiectele sale. Practica a demonstrat c a aceast a tehnic a este una din principalele furnizoare de erori ce duc la funct ionarea defectuoas a a programelor. In Java programatorul nu mai are responsabilitatea distrugerii obiectelor sale ntruc at, n momentul rul arii unui program, simultan cu interpretorul Java, ruleaz a si un proces care se ocup a cu distrugerea obiectelor care nu mai sunt folosite. Acest proces pus la dispozit ie de platforma Java de lucru se nume ste garbage collector (colector de gunoi), prescurtat gc. Un obiect este eliminat din memorie de procesul de colectare atunci c and nu mai exist a nici o referint a la acesta. Referint ele (care sunt de fapt variabile) sunt distruse dou a moduri: natural, atunci c and variabila respectiv a iese din domeniul s au de vizibilitate, de exemplu la terminarea metodei n care ea a fost declarat a; explicit, dac a atribuim variabilei respective valoare null.

Cum funct ioneaz a colectorul de gunoaie ? Colectorul de gunoaie este un proces de prioritate scazut a care se execut a periodic, scaneaz a dinamic memoria ocupat a de programul Java aat n execut ie si marcheaz a acele obiecte care au referint e directe sau indirecte. Dup a ce toate obiectele au fost parcurse, cele care au r amas nemarcate sunt eliminate automat din memorie.

2.2. CREAREA CLASELOR

39

Apelul metodei gc din clasa System sugereaz a ma sinii virtuale Java s a depun a eforturi n recuperarea memoriei ocupate de obiecte care nu mai sunt folosite, f ar a a fort a ns a pornirea procesului.

Finalizare Inainte ca un obiect s a e eliminat din memorie, procesul gc d a acelui obiect posibilitatea s a curet e dup a el, apel and metoda de nalizare a obiectului respectiv. Uzual, n timpul naliz arii un obiect si inchide sierele si socket-urile folosite, distruge referint ele c atre alte obiecte (pentru a u ssura sarcina colectorului de gunoaie), etc. Codul pentru nalizarea unui obiect trebuie scris ntr-o metod a special a numita finalize a clasei ce descrie obiectul respectiv. (vezi Clasa Object)

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

2.2
2.2.1

Crearea claselor
Declararea claselor

Clasele reprezint a o modalitate de a introduce noi tipuri de date ntr-o aplicat ie Java, cealalt a modalitate ind prin intermediul interfet elor. Declararea unei clase respect a urm atorul format general: [public][abstract][final]class NumeClasa [extends NumeSuperclasa] [implements Interfata1 [, Interfata2 ... ]] { // Corpul clasei } A sadar, prima parte a declarat iei o ocup a modicatorii clasei. Ace stia sunt:

40

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

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

2.2.2

Extinderea claselor

Spre deosebire de alte limbaje de programare orientate-obiect, Java permite doar mo stenirea simpl a, ceea ce neamn a c a o clas a poate avea un singur p arinte (superclas a). Evident, o clas a poate avea oric ati mo stenitori (subclase), de unde rezult a c a mult imea tuturor claselor denite n Java poate vazut a ca un arbore, r ad acina acestuia ind clasa Object. A sadar, Object este singura clas a care nu are p arinte, ind foarte important a n modul de lucru cu obiecte si structuri de date n Java.

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

41

O subclas a mo stene ste de la p arintele s au toate variabilele si metodele care nu sunt private.

2.2.3

Corpul unei clase

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

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

CAPITOLUL 2. OBIECTE S I CLASE

Variabilele unei clase pot avea acela si nume cu metodele clasei, care poate chiar numele clasei, f ar a a exista posibilitatea aparit iei vreunei ambiguit a ti din punctul de vedere al compilatorului. Acest lucru este ns a total nerecomandat dac a ne g andim din perspectiva lizibilit a tii (clarit a tii) codului, dovedind un stil inecient de progamare. class A { int A; void A() {}; // Corect pentru compilator // Nerecomandat ca stil de programare }

Atent ie Variabilele si metodele nu pot avea ca nume un cuv ant cheie Java.

2.2.4

Constructorii unei clase

Constructorii unei clase sunt metode speciale care au acela si nume cu cel al clasei, nu returneaz a nici o valoare si sunt folosit i pentru init ializarea obiectelor acelei clase n momentul instant ierii lor. class NumeClasa { [modificatori] NumeClasa([argumente]) { // Constructor

2.2. CREAREA CLASELOR } }

43

O clas a poate avea unul sau mai mult i constructori care trebuie ns a s a difere prin lista de argumente primite. In felul acesta sunt permise diverse tipuri de init ializ ari ale obiectelor la crearea lor, n funct ie de num arul parametrilor cu care este apelat constructorul. S a consider am ca exemplu declararea unei clase care descrie not iunea de dreptunghi si trei posibili constructori pentru aceasta clas 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 apelat i automat la instant ierea unui obiect. In cazul n care dorim s a apel am explicit constructorul unei clase folosim expresia this( argumente ), care apeleaz a constructorul corespunz ator (ca argumente) al clasei respective. Aceast a metod a este folosit a atunci c and sunt implementat i mai mult i constructori pentru o clas a, pentru a nu repeta secvent ele de cod scrise deja la constructorii cu mai multe argumente (mai generali). Mai ecient, f ar a

44

CAPITOLUL 2. OBIECTE S I CLASE

a repeta acelea si secvent e de cod n tot i constructorii (cum ar a sarea mesajului Instantiere dreptunghi), clasa de mai sus poate rescris a astfel: 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 a putem apela explicit constructorii superclasei cu expresia super( argumente ). S a presupunem c a dorim s a cre am clasa Patrat, derivat a din clasa Dreptunghi: class Patrat extends Dreptunghi { Patrat(double x, double y, double d) { super(x, y, d, d); // Apelam constructorul superclasei } }

Atent ie Apelul explcit al unui constructor nu poate ap area dec at ntr-un alt constructor si trebuie s a e prima instruct iune din constructorul respectiv.

2.2. CREAREA CLASELOR

45

Constructorul implicit Constructorii sunt apelat i automat la instant ierea unui obiect. In cazul n care scriem o clas a care nu are declarat nici un constructor, sistemul i creeaz a automat un constructor implicit, care nu prime ste nici un argument si care nu face nimic. Deci prezent a constructorilor n corpul unei clase nu este obligatorie. Dac a ns a scriem un constructor pentru o clas a, care are mai mult de un argument, atunci constructorul implicit (f ar a nici un argument) nu va mai furnizat implicit de c atre sistem. S a consider am, ca exemplu, urm atoarele declarat ii de clase: 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 a consider am acum dou a instant ieri ale claselor de mai sus: 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 mo stenirii unei clase, instant ierea unui obiect din clasa extins a implic a instant ierea unui obiect din clasa p arinte. Din acest motiv, ecare constructor al clasei u va trebui s a aib a un constructor cu aceea si signatur a n p arinte sau s a apeleze explicit un constructor al clasei extinse folosind expresia super([argumente]), n caz contrar ind semnalat a o eroare la 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 S I CLASE

Constructorii unei clase pot avea urm atorii modicatori de acces: public, protected, private si cel implicit. public In orice alt a clas a se pot crea instant e ale clasei respective. protected Doar n subclase pot create obiecte de tipul clasei respective. private In nici o alt a clas a nu se pot instant ia obiecte ale acestei clase. O astfel de clas a poate cont ine metode publice (numite factory methods) care s a e responsabile cu crearea obiectelor, control and n felul acesta diverse aspecte legate de instant ierea clasei respective. implicit Doar n clasele din acela si pachet se pot crea instant e ale clasei respective.

2.2.5

Declararea variabilelor

Variabilele membre ale unei clase se declar a de obicei naintea metodelor, de si acest lucru nu este impus de c atre compilator.

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

47

Variabilele membre ale unei clase se declar a n corpul clasei si nu n corpul unei metode, ind vizibile n toate metodele respectivei clase. Variabilele declarate n cadrul unei metode sunt locale metodei respective. Declararea unei variabile presupune specicarea urm atoarelor lucruri: numele variabilei tipul de date al acesteia nivelul de acces la acea variabila din alte clase dac a este constant a sau nu dac a este variabil a de instant a sau de clas a alt i modicatori Generic, o variabil a se declar a astfel: [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 declarat ii 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 S I CLASE

S a analiz am modicatorii care pot specicat i pentru o variabil a, alt ii dec at cei de acces care sunt tratati ntr-o sect iune separata: Specicatori de acces pentru membrii unei clase. static Prezent a lui declar a c a o variabil a este variabil a de clas a si nu de instant a. (vezi Membri de instant a si membri de clas a) int variabilaInstanta ; static int variabilaClasa; nal Indic a faptul c a valoarea variabilei nu mai poate schimbat a, cu alte cuvinte este folosit pentru declararea constantelor. final double PI = 3.14 ; ... PI = 3.141; // Eroare la compilare ! Prin convent ie, numele variabilelor nale se scriu cu litere mari. Folosirea lui final aduce o exibilitate sporit a n lucrul cu constante, n sensul c a valoarea unei variabile nu trebuie specicat a neap arat la declararea ei (ca n exemplul de mai sus), ci poate specicat a si ulterior ntr-un constructor, dup a care ea nu va mai putea modicat 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 a la serializare. (vezi Serializarea obiectelor)

2.2. CREAREA CLASELOR

49

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

2.2.6

this si super

Sunt variabile predenite care fac referint a, n cadrul unui obiect, la obiectul propriu-zis (this), respectiv la instant a p arintelui (super). Sunt folosite n general pentru a rezolva conicte de nume prin referirea explicit a a unei variabile sau metode membre. Dup a cum am v azut, utilizate sub form a de metode au rolul de a apela constructorii corespunz atori ca argumente ai clasei 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 S I CLASE

2.3
2.3.1

Implementarea metodelor
Declararea metodelor

Metodele sunt responsabile cu descrierea comportamentului unui obiect. Intruc at Java este un limbaj de programare complet orientat-obiect, metodele se pot g asi doar n cadrul claselor. Generic, o metod a se declar a astfel: [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 a analiz am modicatorii care pot specicat i pentru o metod a, alt ii dec at cei de acces care sunt tratat i ntr-o sect iune separat a. static Prezent a lui declar a c a o metod a este de clas a si nu de instant a. (vezi Membri de instant a si membri de clas a) void metodaInstanta(); static void metodaClasa(); abstract Permite declararea metodelor abstracte. O metod a abstract a este o metod a care nu are implementare si trebuie obligatoriu s a fac a parte dintr-o clas a abstract a. (vezi Clase si metode abstracte)

2.3. IMPLEMENTAREA METODELOR

51

nal Specic a faptul c a acea metoda nu mai poate supradenit a n subclasele clasei n care ea este denit a ca ind nal a. Acest lucru este util dac a respectiva metod a are o implementare care nu trebuie schimbat a sub nici o form a n subclasele ei, ind critic a pentru consistent a st arii unui obiect. De exemplu, student ilor unei universit a ti trebuie s a li se calculeze media nala, n funct ie de notele obt inute la examene, n aceea si manier a, indiferent de facultatea la care sunt.

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 n care avem o libr arie important a de funct ii scrise n alt limbaj de programare, cum ar C, C++ si limbajul de asamblare, acestea pot refolosite din programele Java. Tehnologia care permite acest lucru se nume ste JNI (Java Native Interface) si permite asocierea dintre metode Java declarate cu native si metode native scrise n limbajele de programare ment ionate. synchronized Este folosit n cazul n care se lucreaz a cu mai multe re de execut ie iar metoda respectiv a gestioneaz a resurse comune. Are ca efect construirea unui monitor care nu permite executarea metodei, la un moment dat, dec at unui singur r de execut ie. (vezi Fire de executie)

52

CAPITOLUL 2. OBIECTE S I CLASE

2.3.2

Tipul returnat de o metod a

Metodele pot sau nu s a returneze o valoare la terminarea lor. Tipul returnat poate at at un tip primitiv de date sau o referint a la un obiect al unei clase. In cazul n care o metod a nu returneaz a nimic atunci trebuie obligatoriu specicat cuv antul cheie void ca tip returnat: public void afisareRezultat() { System.out.println("rezultat"); } private void deseneaza(Shape s) { ... return; } Dac a o metod a trebuie s a returneze o valoare acest lucru se realizeaz a prin intermediul instruct iunii return, care trebuie s a apar a n toate situat iile de terminare a funct iei. 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 n care n declarat ia funct iei tipul returnat este un tip primitiv de date, valoarea returnat a la terminarea funct iei trebuie s a aib a obligatoriu acel tip sau un subtip al s au, altfel va furnizat a o eroare la compilare. In general, orice atribuire care implic a pierderi de date este tratat a de compilator ca 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 a valoarea returnat a este o referint a la un obiect al unei clase, atunci clasa obiectului returnat trebuie s a coincid a sau s a e o subclas a a clasei specicate la declararea metodei. De exemplu, e clasa Poligon si subclasa 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 c atre o metod a

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

54

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

Spre deosebire de alte limbaje, n Java nu pot trimise ca parametri ai unei metode referint e la alte metode (funct ii), ns a pot trimise referint e la obiecte care s a cont in a implementarea acelor metode, pentru a apelate. P ana la aparit ia versiunii 1.5, n Java o metod a nu putea primi un num ar variabil de argumente, ceea ce nseamna c a apelul unei metode trebuia s a se fac a cu specicarea exact a a numarului si tipurilor argumentelor. Vom analiza ntr-o sect iune separat a modalitate de specicare a unui num ar variabil de argumente pentru o metod a. Numele argumentelor primite trebuie s a difere ntre ele si nu trebuie s a coincid a cu numele nici uneia din variabilele locale ale metodei. Pot ns a s a coincid a cu numele variabilelor membre ale clasei, caz n care diferent ierea 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 a c a metoda recept ioneaz a doar valorile variabilelor primite ca parametri. C and argumentul are tip primitiv de date, metoda nu-i poate schimba valoarea dec at local ( n cadrul metodei); la revenirea din metod a variabila are aceea si valoare ca naintea apelului, modic arile f acute n cadrul metodei ind pierdute. C and argumentul este de tip referint a, metoda nu poate schimba valoarea referint ei obiectului, ns a poate apela metodele acelui obiect si poate modica orice variabil a membr a accesibil a. A sadar, dac a dorim ca o metod a s a schimbe starea (valoarea) unui argument primit, atunci el trebuie s a e neaparat de tip referint a.

2.3. IMPLEMENTAREA METODELOR

55

De exemplu, s a consider am clasa Cerc descris a anterior n care dorim s a implement am o metod a care s a returneze parametrii cercului. // 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 a metod a nu va realiza lucrul propus ntruc at ea prime ste doar valorile variabilelor valx, valy si valr si nu referint e la ele (adresele lor de memorie), astfel nc at s a le poat a modica valorile. In concluzie, metoda nu realizeaz a nimic pentru c a nu poate schimba valorile variabilelor primite ca argumente. Pentru a rezolva lucrul propus trebuie s a denim o clas a suplimentar a care s a descrie parametrii pe care dorim s a-i a am: // 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 a si, de si nu i schimb am valoarea (valoarea sa este adresa de memorie la care se gase ste si nu poate schimbat a),

56

CAPITOLUL 2. OBIECTE S I CLASE

putem schimba starea obiectului, adic a informat ia propriu-zis a cont inut a de acesta.

Varianta de mai sus a fost dat a pentru a clarica modul de trimitere a argumentelor unei metode. Pentru a aa ns a valorile variabilelor care descriu starea unui obiect se folosesc metode de tip getter nsot ite de metode setter care s a permit a schimbarea st arii obiectului: 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 num ar variabil de argumente

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

args reprezint a un vector av and tipul specicat si instant iat cu un num ar variabil de argumente, n funct ie de apelul metodei. Tipul argumentelor poate referint a sau primitiv. Metoda de mai jos a seaz a argumentele primite, 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 nc arcarea si supradenirea metodelor

Supra nc arcarea si supradenirea metodelor sunt dou a concepte extrem de utile ale program arii orientate obiect, cunoscute si sub denumirea de polimorsm, si se refer a la: supra ncarcarea (overloading) : n cadrul unei clase pot exista metode cu acela si nume cu condit ia ca signaturile lor s a e diferite (lista de argumente primite s a difere e prin num arul argumentelor, e prin tipul lor) astfel nc at la apelul funct iei cu acel nume s a se poat a stabili n mod unic care dintre ele se execut a. supradenirea (overriding): o subclas a poate rescrie o metod a a clasei p arinte prin implementarea unei metode cu acela si nume si aceea si signatur a ca ale superclasei. 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 a supradenit a poate s a: ignore complet codul metodei corespunz atoare din superclas a (cazul de mai sus): B b = new B(); b.metoda(); // Afiseaza "B: metoda fara parametru"

58

CAPITOLUL 2. OBIECTE S I CLASE extind a codul metodei p arinte, execut and nainte de codul propriu si funct ia p arintelui: 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 a nu poate supradeni o metod a declarat a nal a n clasa p arinte. Orice clas a care nu este abstract a trebuie obligatoriu s a supradeneasc a metodele abstracte ale superclasei (dac a este cazul). In cazul n care o clas a nu supradene ste toate metodele abstracte ale p arintelui, ea ns a si este abstract a si va trebui declarat a ca atare. In Java nu este posibil a supra nc arcarea operatorilor.

2.4

Modicatori de acces

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

S 2.5. MEMBRI DE INSTANT A I MEMBRI DE CLASA

59

A sadar, dac a nu este specicat nici un modicator de acces, implicit nivelul de acces este la nivelul pachetului. In cazul n care declar am un membru protected atunci accesul la acel membru este permis din subclasele clasei n care a fost declarat dar depinde si de pachetul n care se gase ste subclasa: dac a sunt n acela si pachet accesul este permis, dac a nu sunt n acela si pachet accesul nu este permis dec at pentru obiecte de tipul subclasei. Exemple de declarat ii: private int secretPersonal; protected String secretDeFamilie; public Vector pentruToti; long doarIntrePrieteni; private void metodaInterna(); public String informatii();

2.5

Membri de instant a si membri de clas a

O clas a Java poate cont ine dou a tipuri de variabile si metode : de instant a: declarate f ar a modicatorul static, specice ec arei instant e create dintr-o clas a si de clas a: declarate cu modicatorul static, specice clasei.

2.5.1

Variabile de instant a si de clas a

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

60

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

A sadar, ecare obiect nou creat va putea memora valori diferite pentru variabilele sale de instant a. Pentru variabilele de clas a (statice) sistemul aloc a o singur a zon a de memorie la care au acces toate instant ele clasei respective, ceea ce nseamn a c a dac a un obiect modic a valoarea unei variabile statice ea se va modica si pentru toate celelalte obiecte. Deoarece nu depind de o anumit a instant aa unei clase, variabilele statice pot referite si sub forma: 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

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

S 2.5. MEMBRI DE INSTANT A I MEMBRI DE CLASA

61

2.5.2

Metode de instant a si de clas a

Similar ca la variabile, metodele declarate f ar a modicatorul static sunt metode de instant a iar cele declarate cu static sunt metode de clas a (statice). Diferent a ntre cele dou a tipuri de metode este urm atoarea: metodele de instant a opereaz a at at pe variabilele de instant a c at si pe cele statice ale clasei; metodele de clas a opereaz a doar pe variabilele statice ale clasei. 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 si la variabilele statice, ntruc at metodele de clas a nu depind de starea obiectelor clasei respective, apelul lor se poate face si sub forma: NumeClasa.numeMetodaStatica Exemplu.metodaStatica(); // Corect, echivalent cu Exemplu obj = new Exemplu(); obj.metodaStatica(); // Corect, de asemenea Metodele de instant a nu pot apelate dec at pentru un obiect al clasei respective: Exemplu.metodaDeInstanta(); // Eroare la compilare ! Exemplu obj = new Exemplu(); obj.metodaDeInstanta(); // Corect

62

CAPITOLUL 2. OBIECTE S I CLASE

2.5.3

Utilitatea membrilor de clas a

Membrii de clas a sunt folosit i pentru a pune la dispozit ie valori si metode independente de starea obiectelor dintr-o anumita clas a.

Declararea ecient a a constantelor S a consider am situat ia c and dorim s a declar am o constant a. class Exemplu { final double PI = 3.14; // Variabila finala de instanta } La ecare instant iere a clasei Exemplu va rezervat a o zon a de memorie pentru variabilele nale ale obiectului respectiv, ceea ce este o risip a ntruc at aceste constante au acelea si valori pentru toate instant ele clasei. Declararea corect a a constantelor trebuie a sadar facut a cu modicatorii static si final, pentru a le rezerva o singur a zon a de memorie, comun a tuturor obiectelor: class Exemplu { static final double PI = 3.14; // Variabila finala de clasa }

Num ararea obiectelor unei clase Num ararea obiectelor unei clase poate f acut a extrem de simplu folosind o variabil a static a si este util a n situat iile c and trebuie s a control am diver si parametri legat i de crearea obiectelor unei clase. class Exemplu { static long nrInstante = 0; Exemplu() { // Constructorul este apelat la fiecare instantiere nrInstante ++; } }

S 2.5. MEMBRI DE INSTANT A I MEMBRI DE CLASA

63

Implementarea funct iilor globale Spre deosebire de limbajele de programare procedurale, n Java nu putem avea funct ii globale denite ca atare, ntruc at orice este un obiect. Din acest motiv chiar si metodele care au o funct ionalitate global a trebuie implementate n cadrul unor clase. Acest lucru se va face prin intermediul metodelor de clas a (globale), deoarece acestea nu depind de starea particular a a obiectelor din clasa respectiv a. De exemplu, s a consider am funct ia sqrt care extrage radicalul unui num ar si care se g ase ste n clasa Math. Dac a nu ar fost funct ie de clas a, apelul ei ar trebuit f acut astfel (incorect, de altfel): // Incorect ! Math obj = new Math(); double rad = obj.sqrt(121); ceea ce ar fost extrem de nepl acut... Fiind ns a metod a static a ea poate apelat a prin: Math.sqrt(121) . A sadar, funct iile globale necesare unei aplicat ii vor grupate corespunz ator n diverse clase si implementate ca metode statice.

2.5.4

Blocuri statice de init ializare

Variabilele statice ale unei clase sunt init ializate la un moment care precede prima utilizare activ a a clasei respective. Momentul efectiv depinde de implementarea ma sinii virtuale Java si poart a numele de init ializarea clasei. Pe l ang a setarea valorilor variabilelor statice, n aceast a etap a sunt executate si blocurile statice de init ializare ale clasei. Acestea sunt secvent e de cod de forma: static { // Bloc static de initializare; ... } care se comport a ca o metod a static a apelat a automat de c atre ma sina virtual a. Variabilele referite ntr-un bloc static de init ializare trebuie s a e obligatoriu de clas a sau locale blocului: public class Test { // Declaratii de variabile statice

64 static int x = 0, y, z;

CAPITOLUL 2. OBIECTE S I 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 a imbricat a este, prin denit ie, o clas a membr a a unei alte clase, numit a si clas a de acoperire. In funct ie de situat ie, denirea unei clase interne se poate face e ca membru al clasei de acoperire - caz n care este accesibil a tuturor metodelor, e local n cadrul unei metode. class ClasaDeAcoperire{ class ClasaImbricata1 { // Clasa membru } void metoda() { class ClasaImbricata2 { // Clasa locala metodei } }

2.6. CLASE IMBRICATE }

65

Folosirea claselor imbricate se face atunci c and o clas a are nevoie n implementarea ei de o alt a clas a si nu exist a nici un motiv pentru care aceasta din urm a s a e declarat a de sine st at atoare (nu mai este folosit a nic aieri). O clas a imbricat a are un privilegiu special fat a de celelalte clase si anume acces nerestrict ionat la toate variabilele clasei de acoperire, chiar dac a acestea sunt private. O clas a declarat a local a unei metode va avea acces si la variabilele nale declarate n metoda respectiv 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 a imbricat a membr a (care nu este local a unei metode) poate referit a din exteriorul clasei de acoperire folosind expresia ClasaDeAcoperire.ClasaImbricata A sadar, clasele membru pot declarate cu modicatorii public, protected, private pentru a controla nivelul lor de acces din exterior, ntocmai ca orice variabil a sau metod a mebr a a clasei. Pentru clasele imbricate locale unei metode nu sunt permi si acest i modicatori. Toate clasele imbricate pot declarate folosind modicatorii abstract si final, semnicat ia lor ind aceea si ca si n cazul claselor obi snuite.

66

CAPITOLUL 2. OBIECTE S I CLASE

2.6.2

Clase interne

Spre deosebire de clasele obi snuite, o clas a imbricat a poate declarat a static a sau nu. O clas a imbricat a nestatic a se nume ste clasa intern a. class ClasaDeAcoperire{ ... class ClasaInterna { ... } static class ClasaImbricataStatica { ... } } Diferent ierea acestor denumiri se face deoarece: o clas a imbricat a reect a relat ia sintactic a a dou a clase: codul unei clase apare n interiorul codului altei clase; o clas a intern a reect a relat ia dintre instant ele a dou a clase, n sensul c a o instant a a unei clase interne nu poate exista decat n cadrul unei instant e a clasei de acoperire. In general, cele mai folosite clase imbricate sunt cele interne. A sadar, o clas a intern a este o clas a imbricat a ale carei instant e nu pot exista dec at n cadrul instant elor clasei de acoperire si care are acces direct la tot i membrii clasei sale de acoperire.

2.6.3

Identicare claselor imbricate

Dup a cum stim orice clas a produce la compilare a sa numitele unit a ti de compilare, care sunt siere av and numele clasei respective si extensia .class si care cont in toate informat iile despre clasa respectiv a. Pentru clasele imbricate aceste unit a ti de compilare sunt denumite astfel: numele clasei de acoperire, urmat de simbolul $ apoi de numele clasei imbricate. class ClasaDeAcoperire{ class ClasaInterna1 {} class ClasaInterna2 {} }

2.7. CLASE S I METODE ABSTRACTE Pentru exemplul de mai sus vor generate trei siere: ClasaDeAcoperire.class ClasaDeAcoperire$ClasaInterna1.class ClasaDeAcoperire$ClasaInterna2.class

67

In cazul n care clasele imbricate au la r andul lor alte clase imbricate (situat ie mai put in uzual a) denumirea lor se face dup a aceea si regul a: ad augarea unui $ si apoi numele clasei imbricate.

2.6.4

Clase anonime

Exist a posibilitatea denirii unor clase imbricate locale, f ar a nume, utilizate doar pentru instant ierea unui obiect de un anumit tip. Astfel de clase se numesc clase anonime si sunt foarte utile n situat ii cum ar crearea unor obiecte ce implementeaz a o anumit a interfat a sau extind o anumit a clas a abstract a. Exemple de folosire a claselor anonime vor date n capitolul Interfet e, precum si extensiv n capitolul Interfat a grac a cu utilizatorul. Fi sierele rezultate n urma compil arii claselor anonime vor avea numele de forma ClasaAcoperire.$1,..., ClasaAcoperire.$n, unde n este num arul de clase anonime denite n clasa respectiv a de acoperire.

2.7

Clase si metode abstracte

Uneori n proiectarea unei aplicat ii este necesar s a reprezent am cu ajutorul claselor concepte abstracte care s a nu poat a instant iate si care s a foloseasc a doar la dezvoltarea ulterioar a a unor clase ce descriu obiecte concrete. De exemplu, n pachetul java.lang exist a clasa abstract a Number care modeleaz a conceptul generic de num ar. Intr-un program nu avem ns a nevoie de numere generice ci de numere de un anumit tip: ntregi, reale, etc. Clasa Number serve ste ca superclas a pentru clasele concrete Byte, Double, Float, Integer, Long si Short, ce implementeaz a obiecte pentru descrierea numerelor de un anumit tip. A sadar, clasa Number reprezint a un concept abstract si nu vom putea instant ia obiecte de acest tip - vom folosi n schimb subclasele sale. Number numar = new Number(); // Eroare Integer intreg = new Integer(10); // Corect

68

CAPITOLUL 2. OBIECTE S I CLASE

2.7.1

Declararea unei clase abstracte

Declararea unei clase abstracte se face folosind cuv antul rezervat abstract: [public] abstract class ClasaAbstracta [extends Superclasa] [implements Interfata1, Interfata2, ...] { // Declaratii uzuale // Declaratii de metode abstracte } O clas a abstract a poate avea modicatorul public, accesul implicit ind la nivel de pachet, dar nu poate specica modicatorul final, combinat ia abstract final ind semnalat a ca eroare la compilare - de altfel, o clas a declarat a astfel nu ar avea nici o utilitate. O clas a abstract a poate cont ine acelea si elemente membre ca o clas a obi snuit a, la care se adaug a declarat ii de metode abstracte - f ar a nici o implementare.

2.7.2

Metode abstracte

Spre deosebire de clasele obi snuite care trebuie s a furnizeze implement ari pentru toate metodele declarate, o clas a abstract a poate cont ine metode f ar a nici o implementare. Metodele fara nici o implementare se numesc metode abstracte si pot ap area doar n clase abstracte. In fat a unei metode abstracte trebuie s a apar a obligatoriu cuv antul cheie abstract, altfel va furnizat ao eroare de compilare. abstract class ClasaAbstracta { abstract void metodaAbstracta(); // Corect void metoda(); // Eroare } In felul acesta, o clas a abstract a poate pune la dispozit ia subclaselor sale un model complet pe care trebuie s a-l implementeze, furniz and chiar implementarea unor metode comune tuturor claselor si l as and explicitarea altora

2.7. CLASE S I METODE ABSTRACTE

69

ec arei subclase n parte. Un exemplu elocvent de folosire a claselor si metodelor abstracte este descrierea obiectelor grace ntr-o manier a orientat a-obiect. Obiecte grace: linii, dreptunghiuri, cercuri, curbe Bezier, etc St ari comune: pozit ia(originea), dimensiunea, culoarea, etc Comportament: mutare, redimensionare, desenare, colorare, etc. Pentru a folosi st arile si comportamentele comune acestor obiecte n avantajul nostru putem declara o clas a generic a GraphicObject care s a e superclas a pentru celelalte clase. Metodele abstracte vor folosite pentru implementarea comportamentului specic ec arui obiect, cum ar desenarea iar cele obi snuite pentru comportamentul comun tuturor, cum ar schimbarea originii. Implementarea clasei abstracte GraphicObject ar putea ar ata 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 S I CLASE

O subclas a care nu este abstract a a unei clase abstracte trebuie s a furnizeze obligatoriu implement ari ale metodelor abstracte denite n superclas a. Implementarea 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 ment ionate urm atoarele: O clas a abstract a poate s a nu aib a nici o metod a abstract a. O metod a abstract a nu poate ap area dec at ntr-o clas a abstract a. Orice clas a care are o metod a abstract a trebuie declarat a ca ind abstract 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 a tipurilor referint a numerice Reader, Writer: superclasele abstracte ale uxurilor de intrare/ie sire pe caractere InputStream, OutputStream: superclasele abstracte ale uxurilor de intrare/ie sire pe octet i AbstractList, AbstractSet, AbstractMap: superclase abstracte pentru structuri de date de tip colect ie

2.8. CLASA OBJECT

71

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

2.8
2.8.1

Clasa Object
Orice clas a are o superclas a

Dup a cum am v azut n sect iunea dedicat a modalit a tii de creare a unei clase, clauza extends specic a faptul c a acea clas a extinde (mo stene ste) o alt a clas a, numit a superclas a. O clas a poate avea o singur a superclas a (Java nu suport a mo stenirea multipl a) si chiar dac a nu specic am clauza extends la crearea unei clase ea totu si va avea o superclas a. Cu alte cuvinte, n Java orice clas a are o superclas a si numai una. Evident, trebuie s a existe o except ie de la aceast a regul a si anume clasa care reprezint a r ad acina ierarhiei format a de relat iile de mo stenire dintre clase. Aceasta este clasa Object. Clasa Object este si superclasa implicit a a claselor care nu specic a o anumit a superclas a. Declarat iile de mai jos sunt echivalente: class Exemplu {} class Exemplu extends Object {}

2.8.2

Clasa Object

Clasa Object este cea mai general a dintre clase, orice obiect ind, direct sau indirect, descendent al acestei clase. Fiind p arintele tuturor, Object dene ste si implementeaz a comportamentul comun al tuturor celorlalte clase Java, cum ar : posibilitatea test arii egalit a tii valorilor obiectelor, specicarea unei reprezent ari ca sir de caractere a unui obiect , returnarea clasei din care face parte un obiect, noticarea altor obiecte c a o variabil a de condit ie s-a schimbat, etc.

72

CAPITOLUL 2. OBIECTE S I CLASE

Fiind subclas a a lui Object, orice clas a i poate supradeni metodele care nu sunt nale. Metodele cel mai uzual supradenite sunt: clone, equals/hashCode, finalize, toString. clone Aceast a metod a este folosit a pentru duplicarea obiectelor (crearea unor clone). Clonarea unui obiect presupune crearea unui nou obiect de acela si tip si care s a aib a aceea si stare (acelea si valori pentru variabilele sale). equals, hashCode Acestea sunt, de obicei, supradenite mpreun a. In metoda equals este scris codul pentru compararea egalit a tii cont inutului a dou a obiecte. Implicit (implementarea din clasa Object), aceast a metod a compar a referint ele obiectelor. Uzual este redenit a pentru a testa dac a st arile obiectelor coincid sau dac a doar o parte din variabilele lor coincid. Metoda hashCode returneaza un cod ntreg pentru ecare obiect, pentru a testa consistent a obiectelor: acela si obiect trebuie s a returneze acela si cod pe durata execut iei programului. Dac a dou a obiecte sunt egale conform metodei equals, atunci apelul metodei hashCode pentru ecare din cele dou a obiecte ar trebui s a returneze acela si intreg. nalize In aceast a metod a se scrie codul care cur a t a dup a un obiect nainte de a eliminat din memorie de colectorul de gunoaie. (vezi Distrugerea obiectelor) toString Este folosit a pentru a returna o reprezentare ca sir de caractere a unui obiect. Este util a pentru concatenarea sirurilor cu diverse obiecte n vederea a s arii, ind apelat a automat atunci c and este necesar a transformarea unui obiect n sir de caractere. Exemplu obj = new Exemplu(); System.out.println("Obiect=" + obj); //echivalent cu System.out.println("Obiect=" + obj.toString());

2.8. CLASA OBJECT

73

S a consider am urm atorul exemplu, n care implement am part ial clasa numerelor complexe, si n care vom supradeni metode ale clasei Object. De asemenea, vom scrie un mic program TestComplex n care vom testa 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 S I 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 a cum v azut tipurile Java de date pot mp art ie n primitive si referint a. Pentru ecare tip primitiv exist a o clas a corespunz atoare care permie lucrul 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 init ializarea unui obiect av and o anumit a valoare primitiv a si metode specializate pentru conversia unui obiect n tipul primitiv corespunz ator, de genul tipPrimitivValue: Integer obi = new Integer(1); int i = obi.intValue(); Boolean obb = new Boolean(true); boolean b = obb.booleanValue(); Incep and cu versiunea 1.5 a limbajului Java, atribuirile explicite ntre tipuri primitve si referint a sunt posibile, acest mecanism purt and numele de autoboxing, respectiv auto-unboxing. Conversia explicit a va facut a de c atre 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

Incep and cu versiunea 1.5 a limbajului Java, exist a posibilitatea de a deni tipuri de date enumerare prin folosirea cuv antului cheie enum. Acest a solut ie simplic a manevrarea grupurilor de constante, dup a cum reiese din urm atorul exemplu: 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 a astfel: 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 a corespunz atoare.

76

CAPITOLUL 2. OBIECTE S I CLASE

Capitolul 3 Except ii
3.1 Ce sunt except iile ?

Termenul except ie este o prescurtare pentru eveniment except ional si poate denit ca un eveniment ce se produce n timpul execut iei unui program si care provoac a ntreruperea cursului normal al execut iei acestuia. Except iile pot ap area din diverse cauze si pot avea nivele diferite de gravitate: de la erori fatale cauzate de echipamentul hardware p an a la erori ce tin strict de codul programului, cum ar accesarea unui element din afara spat iului alocat unui vector. In momentul c and o asemenea eroare se produce n timpul execut iei va generat un obiect de tip except ie ce cont ine: informat ii despre except ia respectiv a; starea programului n momentul producerii acelei except ii. 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 a o except ie, programul se va opri la instruct iunea care a cauzat except ia si se va a sa un mesaj de eroare de genul: 77

78

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

Crearea unui obiect de tip except ie se nume ste aruncarea unei except ii (throwing an exception). In momentul n care o metod a genereaz a (arunc a) o except ie sistemul de execut ie este responsabil cu g asirea unei secvent e de cod dintr-o metod a care s a o trateze. C autarea se face recursiv, ncep and cu metoda care a generat except ia si merg and napoi pe linia apelurilor c atre acea metod a. Secvent a de cod dintr-o metod a care trateaz a o anumit a except ie se nume ste analizor de except ie (exception handler) iar interceptarea si tratarea ei se nume ste prinderea except iei (catch the exception). Cu alte cuvinte, la aparit ia unei erori este aruncat a o except ie iar cineva trebuie s a o prind a pentru a o trata. Dac a sistemul nu gase ste nici un analizor pentru o anumit a except ie, atunci programul Java se opre ste cu un mesaj de eroare ( n cazul exemplului de mai sus mesajul Aici nu se mai ajunge... nu va a sat).

Atent ie In Java tratarea erorilor nu mai este o opt iune ci o constr angere. In aproape toate situat ile, o secvent a de cod care poate provoca except ii trebuie s a specice modalitatea de tratare a acestora.

3.2

Prinderea si tratarea except iilor

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

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

79

S a consider am urm atorul exemplu: citirea unui sier octet cu octet si asarea lui pe ecran. F ar a a folosi tratarea except iilor metoda responsabil a cu citirea sierului ar ar ata astfel: 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 a secvent a de cod va furniza erori la compilare deoarece n Java tratarea erorilor este obligatorie. Folosind mecanismul except iilor metoda citeste si poate trata singur a erorile care pot surveni pe parcursul execut iei sale. Mai jos este codul complte si corect al unui program ce a seaz a pe ecran cont inutul unui sier al c arui nume este primit ca argument de la linia de comand a. Tratarea except iilor este realizat a complet chiar de c atre metoda citeste.

80

CAPITOLUL 3. EXCEPT II 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 NotFo undEx cepti on 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 S I TRATAREA EXCEPT IILOR


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

81

Blocul try contine instruct iunile de deschidere a unui sier si de citire dintr-un sier, ambele put and produce except ii. Except iile provocate de aceste instruct iuni sunt tratate n cele dou a blocuri catch, c ate unul pentru ecare tip de except ie. Inchiderea sierului se face n blocul nally, deoarece acesta este sigur c a se va executa F ar a a folosi blocul nally, nchiderea sierului ar trebuit facut a n ecare situat ie n care sierul ar 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 a mai delicat a care trebuie semnalata n aceasta situat ie este faptul c a metoda close, responsabil a cu nchiderea unui sier, poate provoca la r andul s au except ii, de exemplu atunci c and sierul mai este folosit si de alt proces si nu poate nchis. Deci, pentru a avea un cod complet corect trebuie s a trat am si posibilitatea aparit iei unei except ii la metoda close.

Atent ie Obligatoriu un bloc de instruct iuni try trebuie s a e urmat de unul sau mai multe blocuri catch, n funct ie de except iile provocate de acele instruct iuni sau (opt ional) de un bloc nally.

82

CAPITOLUL 3. EXCEPT II

3.3

Aruncarea except iilor

In cazul n care o metod a nu si asum a responsabilitatea trat arii uneia sau mai multor except ii pe care le pot provoca anumite instruct iuni din codul s au atunci ea poate s a arunce aceste except ii c atre metodele care o apeleaz a, urm and ca acestea s a implementeze tratarea lor sau, la r andul lor, s a arunce mai departe except iile respective. Acest lucru se realizeaz a prin specicarea n declarat ia metodei a clauzei throws: [modificatori] TipReturnat metoda([argumente]) throws TipExceptie1, TipExceptie2, ... { ... }

Atent ie O metod a care nu trateaz a o anumit a except ie trebuie obligatoriu s ao arunce.

In exemplul de mai sus dac a nu facem tratarea except iilor n cadrul metodei citeste atunci metoda apelant a (main) va trebui s a fac a acest lucru: 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 EXCEPT IILOR

83

public static void main ( String args []) { if ( args . length > 0) { try { citesteFisier ( args [0]) ; } catch ( File NotFo undEx cepti 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 ! " ) ; } }

Observat i c a, n acest caz, nu mai putem diferent ia except iile provocate de citirea din sier s de inchiderea sierului, ambele ind de tipul IOException. De asemenea, inchiderea sierului nu va mai facut a n situatia n care apare o except ie la citirea din sier. Este situat ia n care putem folosi blocul finally f ar a a folosi nici un bloc catch: 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. EXCEPT II

Metoda apelant a poate arunca la r andul s au except iile mai departe c atre metoda care a apelat-o la r andul ei. Aceast a nl ant uire se termin a cu metoda main care, dac a va arunca except iile ce pot ap area n corpul ei, va determina trimiterea except iilor c atre ma sina virtual a Java. public void metoda3 throws TipExceptie { ... } public void metoda2 throws TipExceptie { metoda3(); } public void metoda1 throws TipExceptie { metoda2(); } public void main throws TipExceptie { metoda1(); } Tratarea except iilor de c atre JVM se face prin terminarea programului si a sarea informat iilor despre except ia care a determinat acest lucru. Pentru exemplul nostru, metoda main ar putea declarat a astfel: public static void main(String args[]) throws FileNotFoundException, IOException { citeste(args[0]); } Intotdeauna trebuie g asit compromisul optim ntre tratarea local a a except iilor si aruncarea lor c atre nivelele superioare, astfel nc at codul s a e c at mai clar si identicarea locului n care a ap arut except ia s a e c at mai u sor de f acut.

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

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

85

Aceast a instruct iune este folosit a mai ales la aruncarea except iilor proprii. (vezi Crearea propriilor except ii)

3.4

Avantajele trat arii except iilor

Prin modalitatea sa de tratare a except iilor, Java are urm atoarele avantaje fat a de mecanismul tradit ional de tratare a erorilor: Separarea codului pentru tratarea unei erori de codul n care ea poate s a apar a. Propagarea unei erori p an a la un analizor de except ii corespunz ator. Gruparea erorilor dup a tipul lor.

3.4.1

Separarea codului pentru tratarea erorilor

In programarea tradit ional a tratarea erorilor se combin a cu codul ce poate produce aparit ia lor produc and a sa numitul cod spaghetti. S a consider am urm atorul exemplu: o funct ie care ncarc a un sier n memorie: citesteFisier { deschide fisierul; determina dimensiunea fisierului; aloca memorie; citeste fisierul in memorie; inchide fisierul; } Problemele care pot ap area la aceasta funct ie, aparent simpl a, sunt de genul: Ce se nt ampl a dac a: ... ? sierul nu poate deschis nu se poate determina dimensiunea sierului

86 nu poate alocat a sucient a memorie nu se poate face citirea din sier sierul nu poate nchis

CAPITOLUL 3. EXCEPT II

Un cod tradit ional care s a trateze aceste erori ar ar ata astfel: 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 si ngreuneaz a extrem de mult nt telegerea sa. In Java, folosind mecansimul except iilor, codul ar arata, schematizat, astfel:

3.4. AVANTAJELE TRATARII EXCEPT IILOR 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 p an a la un analizor de except ii corespunz ator. S a presupunem c a apelul la metoda citesteFisier este consecint a unor apeluri imbricate de metode: int metoda1() { metoda2(); ... } int metoda2() { metoda3; ... } int metoda3 { citesteFisier(); ...

88 }

CAPITOLUL 3. EXCEPT II

S a presupunem de asemenea c a dorim s a facem tratarea erorilor doar n metoda1. Tradit ional, acest lucru ar trebui f acut prin propagarea erorii produse de metoda citesteFisier p an a la metoda1: 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 a cum am vazut, Java permite unei metode s a arunce except iile ap arute n cadrul ei la un nivel superior, adic a funct iilor care o apeleaz a sau sistemului. Cu alte cuvinte, o metod a poate s a nu si asume responsabilitatea trat arii except iilor ap arute n cadrul ei: int metoda1() { try { metoda2(); } catch (TipExceptie e) { //proceseazaEroare; } ... }

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

89

3.4.3

Gruparea erorilor dup a tipul lor

In Java exist a clase corespunz atoare tuturor except iilor care pot ap area la execut ia unui program. Acestea sunt grupate n funct ie de similarit a tile lor ntr-o ierarhie de clase. De exemplu, clasa IOException se ocup a cu except iile ce pot ap area la operat ii de intrare/iesire si diferent iaz a la r andul ei alte tipuri de except ii, cum ar FileNotFoundException, EOFException, etc. La r andul ei, clasa IOException se ncadreaz a ntr-o categorie mai larg a de except ii si anume clasa Exception. Radacin a acestei ierarhii este clasa Throwable (vezi Ierarhia claselor ce descriu except ii). Pronderea unei except ii se poate face e la nivelul clasei specice pentru acea except ie, e la nivelul uneia din superclasele sale, n funct ie de necesit a tile programului, ns a, cu c at clasa folosit a este mai generic a cu at at tratarea except iilor 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. EXCEPT II 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 except ii

R ad acina claselor ce descriu except ii este clasa Throwable iar cele mai importante subclase ale sale sunt Error, Exception si RuntimeException, care sunt la r andul lor superclase pentru o serie ntreag a de tipuri de except ii.

Erorile, obiecte de tip Error, sunt cazuri speciale de except ii generate de funct ionarea anormal a a echipamentului hard pe care ruleaz a un program Java si sunt invizibile programatorilor. Un program Java nu trebuie s a trateze aparit ia acestor erori si este improbabil ca o metod a Java s a provoace asemenea erori.

3.6. EXCEPT II LA EXECUT IE

91

Except iile, obiectele de tip Exception, sunt except iile standard (soft) care trebuie tratate de c atre programele Java. Dup a cum am mai zis tratarea acestor except ii nu este o opt iune ci o constr angere. Except iile care pot sc apa netratate descind din subclasa RuntimeException si se numesc except ii la execut ie. Metodele care sunt apelate uzual pentru un obiect except ie sunt denite n clasa Throwable si sunt publice, astfel nc at pot apelate pentru orice tip de except ie. Cele mai uzuale sunt: getMessage - a seaz a detaliul unei except ii; printStackTrace - a seaz a informat ii complete despre except ie si localizarea ei; toString - metod a mo stenit a din clasa Object, care furnizeaz a reprezentarea ca sir de caractere a except iei.

3.6

Except ii la execut ie

In general, tratarea except iilor este obligatorie n Java. De la acest principu se sustrag ns a a sa numitele except ii la execut ie sau, cu alte cuvinte, except iile care provin strict din vina programatorului si nu generate de o anumit a situat ie extern a, cum ar lipsa unui sier. Aceste except ii au o superclas a comun a RuntimeException si n acesata categorie sunt incluse except iile provocate de: operat ii aritmetice ilegale ( mp art irea ntregilor la zero); ArithmeticException accesarea membrilor unui obiect ce are valoarea null; NullPointerException accesarea eronat a a elementelor unui vector. ArrayIndexOutOfBoundsException Except iile la execut ie pot ap area uriunde n program si pot extrem de numeroare iar ncercarea de prindere a lor ar extrem de anevoioas a. Din acest motiv, compilatorul permite ca aceste except ii s a r am an a netratate, tratarea lor neind ns a ilegal a. Reamintim ns a c a, n cazul aparit iei oric arui tip de except ie care nu are un analizor corespunz ator, programul va terminat.

92

CAPITOLUL 3. EXCEPT II 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...");

Imp art irea la 0 va genera o except ie doar dac a tipul numerelor mp art ite este aritmetic ntreg. In cazul tipurilor reale (float si double) nu va generat a nici o except ie, ci va furnizat ca rezultat o constant a care poate , funct ie 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 except ii

Adeseori poate ap area necesitatea cre arii unor except ii proprii pentru a pune n evident a cazuri speciale de erori provocate de metodele claselor unei libr arii, cazuri care nu au fost prevazute n ierarhia except iilor standard Java. O except ie proprie trebuie s a se ncadreze ns a n ierarhia except iilor Java, cu alte cuvinte clasa care o implementeaz a trebuie s a e subclas a a uneia deja existente n aceasta ierarhie, preferabil una apropiat a ca semnicat ie, sau superclasa Exception.

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

93

S a consider am urm atorul exemplu, n care cre am o clas a ce descrie part ial o stiv a de numere ntregi cu operat iile de ad augare a unui element, respectiv de scoatere a elementului din v arful stivei. Dac a presupunem c a stiva poate memora maxim 100 de elemente, ambele operat ii pot provoca except ii. Pentru a personaliza aceste except ii vom crea o clas a specic a denumit 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 - -]; } }

Secvent a cheie este extends Exception care specic a faptul c a noua clas a ExceptieStiva este subclas a a clasei Exception si deci implementeaz a obiecte ce reprezint a except ii.

94

CAPITOLUL 3. EXCEPT II

In general, codul ad augat claselor pentru except ii proprii este nesemnicativ: unul sau doi constructori care a seaza un mesaj de eroare la ie sirea standard. Procesul de creare a unei noi except ii poate dus mai departe prin ad augarea unor noi metode clasei ce descrie acea except ie, ns a aceasta dezvoltare nu si are rostul n majoritatea cazurilor. Except iile proprii sunt descrise uzual de clase foarte simple, chiar f ar a nici un cod n ele, cum ar : class ExceptieSimpla extends Exception { } Aceast a clas a se bazeaz a pe constructorul implicit creat de compilator ns a nu are constructorul ExceptieSimpla(String s).

Capitolul 4 Intr ari si ie siri


4.1
4.1.1

Introducere
Ce sunt uxurile?

Majoritatea aplicat iilor necesit a citirea unor informat ii care se g asesc pe o surs a extern a sau trimiterea unor informat ii c atre o destinat ie extern a. Informat ia se poate g asi oriunde: ntr-un sier pe disc, n ret ea, n memorie sau n alt program si poate de orice tip: date primitive, obiecte, imagini, sunete, etc. Pentru a aduce informat ii dintr-un mediu extern, un progam Java trebuie s a deschid a un canal de comunicat ie (ux) de la sursa informat iilor ( sier, memorie, socket, etc) si s a citeasc a secvent ial informat iile respective. Similar, un program poate trimite informat ii c atre o destinat ie extern a deschiz and un canal de comunicat ie (ux) c atre acea destinat ie si scriind secvent ial informat iile respective. Indiferent de tipul informat iilor, citirea/scrierea de pe/c atre un mediu extern respect a urm atorul algoritm: deschide canal comunicatie while (mai sunt informatii) { citeste/scrie informatie; } inchide canal comunicatie; Pentru a generaliza, at at sursa extern a a unor date c at si destinat ia lor sunt v azute ca ind ni ste procese care produc, respectiv consum a informat ii. 95

96

S CAPITOLUL 4. INTRARI I IES IRI

Denit ii: Un ux este un canal de comunicat ie unidirect ional ntre dou a procese. Un proces care descrie o surs a extern a de date se nume ste proces produc ator. Un proces care descrie o destinat ie extern a pentru date se nume ste proces consumator. Un ux care cite ste date se nume ste ux de intrare. Un ux care scrie date se nume ste ux de ie sire.

Observat ii: Fluxurile sunt canale de comunicat ie seriale pe 8 sau 16 bit i. Fluxurile sunt unidirect ionale, de la produc ator la consumator. Fiecare ux are un singur proces produc ator si un singur proces consumator. Intre dou a procese pot exista oric ate uxuri, orice proces put and at at producator c at si consumator n acela si timp, dar pe uxuri diferite. Consumatorul si producatorul nu comunic a direct printr-o interfat a de ux ci prin intermediul codului Java de tratare a uxurilor.

Clasele si intefet ele standard pentru lucrul cu uxuri se g asesc n pachetul java.io. Deci, orice program care necesit a operat ii de intrare sau ie sire trebuie s a cont in a instruct iunea de import a pachetului java.io: import java.io.*;

4.1.2

Clasicarea uxurilor

Exist a trei tipuri de clasicare a uxurilor: Dup a direct ia canalului de comunicat ie deschis uxurile se mpart n: uxuri de intrare (pentru citirea datelor) uxuri de ie sire (pentru scrierea datelor) Dup a tipul de date pe care opereaz a: uxuri de octet i (comunicarea serial a se realizeaz a pe 8 bit i) uxuri de caractere (comunicarea serial a se realizeaz a pe 16 bit i)

4.1. INTRODUCERE Dup a act iunea lor:

97

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

4.1.3

Ierarhia claselor pentru lucrul cu uxuri

Clasele r ad acin a pentru ierarhiile ce reprezint a uxuri de caractere sunt: Reader- pentru uxuri de intrare si Writer- pentru uxuri de ie sire. Acestea sunt superclase abstracte pentru toate clasele ce implementeaz a uxuri specializate pentru citirea/scrierea datelor pe 16 bit i si vor cont ine metodele comune tuturor. Ca o regul a general a, toate clasele din aceste ierarhii vor avea terminat ia Reader sau Writer n funct ie de tipul lor, cum ar n exemplele: FileReader, BufferedReader, FileWriter, BufferedWriter, etc. De asemenea, se observ a ca o alt a regul a general a, faptul c a unui ux de intrare XReader i corespunde uzual un ux de ie sire XWriter, ns a acest lucru nu este obligatoriu.

Clasele radacin a pentru ierarhia uxurilor de octet i sunt: InputStream- pentru uxuri de intrare si OutputStream- pentru uxuri de ie sire. Acestea sunt superclase abstracte pentru clase ce implementeaz a uxuri specializate pentru citirea/scrierea datelor pe 8 bit i. Ca si n cazul uxurilor pe caractere denumirile claselor vor avea terminat ia superclasei lor: FileInputStream, BufferedInputStream, FileOutputStream, BufferedOutputStream, etc., ec arui ux de intrare XInputStream corespunz andu-i uzual un ux de ie sire XOutputStream, f ar a ca acest lucru s a e obligatoriu.

98

S CAPITOLUL 4. INTRARI I IES IRI

P an a la un punct, exist a un paralelism ntre ierarhia claselor pentru uxuri de caractere si cea pentru uxurile pe octet i. Pentru majoritatea programelor este recomandat ca scrierea si citirea datelor s a se fac a prin intermediul uxurilor de caractere, deoarece acestea permit manipularea caracterelor Unicode n timp ce uxurile de octet i permit doar lucrul pe 8 biti caractere ASCII.

4.1.4

Metode comune uxurilor

Superclasele abstracte Reader si InputStream denesc metode similare pentru citirea datelor. Reader InputStream int read() int read() int read(char buf[]) int read(byte buf[]) ... ... De asemenea, ambele clase pun la dispozit ie metode pentru marcarea unei locat ii ntr-un ux, saltul peste un num ar de pozit ii, resetarea pozit iei curente, etc. Acestea sunt ns a mai rar folosite si nu vor detaliate. Superclasele abstracte Writer si OutputStream sunt de asemenea paralele, 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 oric arui ux se realizeaz a prin metoda close. In cazul n care aceasta nu este apelat a explicit, uxul va automat nchis de c atre colectorul de gunoaie atunci c and nu va mai exista nici o referint a la el, ns a acest lucru trebuie evitat deoarece, la lucrul cu uxrui cu zon a tampon de memorie, datele din memorie vor pierdute la nchiderea uxului de c atre gc. Metodele referitoare la uxuri pot genera except ii de tipul IOException sau derivate din aceast a clas a, tratarea lor ind obligatorie.

4.2. FOLOSIREA FLUXURILOR

99

4.2

Folosirea uxurilor

A sa cum am v azut, uxurile pot mp art ite n funct ie de activitatea lor n uxuri care se ocup a efectiv cu citirea/scrierea datelor si uxuri pentru procesarea datelor (de ltrare). In continuare, vom vedea care sunt cele mai importante clase din cele dou a categorii si la ce folosesc acestea, precum si modalit a tile de creare si utilizare a uxurilor.

4.2.1

Fluxuri primitive

Fluxurile primitive sunt responsabile cu citirea/scrierea efectiv a a datelor, pun and la dispozit ie implement ari ale metodelor de baz a read, respectiv write, denite n superclase. In funct ie de tipul sursei datelor, ele pot mp art ite astfel: Fi sier FileReader, FileWriter FileInputStream, FileOutputStream Numite si uxuri sier, acestea sunt folosite pentru citirea datelor dintr-un sier, respectiv scrierea datelor ntr-un sier si vor analizate ntr-o sect iune separat a (vezi Fluxuri pentru lucrul cu siere). Memorie CharArrayReader, CharArrayWriter ByteArrayInputStream, ByteArrayOutputStream Aceste uxuri folosesc pentru scrierea/citirea informat iilor n/din memorie si sunt create pe un vector existent deja. Cu alte cuvinte, permit tratarea vectorilor ca surs a/destinat ie pentru crearea unor uxuri de intrare/ie sire. StringReader, StringWriter Permit tratarea sirurilor de caractere aate n memorie ca surs a/destinat ie pentru crearea de uxuri. Pipe PipedReader, PipedWriter PipedInputStream, PipedOutputStream Implementeaz a componentele de intrare/ie sire ale unei conducte de

100

S CAPITOLUL 4. INTRARI I IES IRI date (pipe). Pipe-urile sunt folosite pentru a canaliza ie sirea unui program sau r de execut ie c atre intrarea altui program sau r de execut ie.

4.2.2

Fluxuri de procesare

Fluxurile de procesare (sau de ltrare) sunt responsabile cu preluarea datelor de la un ux primitiv si procesarea acestora pentru a le oferi ntr-o alt a form a, mai util a dintr-un anumit punct de vedere. De exemplu, BufferedReader poate prelua date de la un ux FileReader si s a ofere informat ia dintr-un sier linie cu linie. Fiind primitiv, FileReader nu putea citi dec at caracter cu caracter. Un ux de procesare nu poate folosit dec at mpreun a cu un ux primitiv. Clasele ce descriu aceste uxuri pot mpartite n funct ie de tipul de procesare pe care l efectueaza astfel: Buerizare BufferedReader, BufferedWriter BufferedInputStream, BufferedOutputStream Sunt folosite pentru a introduce un buer n procesul de citire/scriere a informat iilor, reduc and astfel num arul de acces ari la dispozitivul ce reprezint a sursa/destinat ia original a a datelor. Sunt mult mai eciente dec at uxurile f ar a buer si din acest motiv se recomand a folosirea lor ori de c ate ori este posibil (vezi Citirea si scrierea cu zona tampon). Filtrare FilterReader, FilterWriter FilterInputStream, FilterOutputStream Sunt clase abstracte ce denesc o interfat a comun a pentru uxuri care ltreaz a automat datele citite sau scrise (vezi Fluxuri pentru ltrare). Conversie octet i-caractere InputStreamReader, OutputStreamWriter Formeaz a o punte de legatur a ntre uxurile de caractere si uxurile de octet i. Un ux InputStreamReader cite ste octet i dintr-un ux InputStream si i converte ste la caractere, folosind codicarea standard a caracterelor sau o codicare specicat a de program. Similar, un ux OutputStreamWriter converte ste caractere n octet i si trimite rezutatul c atre un ux de tipul OutputStream.

4.2. FOLOSIREA FLUXURILOR

101

Concatenare SequenceInputStream Concateneaz a mai multe uxuri de intrare ntr-unul singur (vezi Concatenarea sierelor). 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 ma sina pe care se lucreaz a (vezi Folosirea claselor DataInputStream si DataOutputStream). Num arare LineNumberReader LineNumberInputStream Ofer a si posibilitatea de num arare automat a a liniilor citite de la un ux de intrare. Citire n avans PushbackReader PushbackInputStream Sunt uxuri de intrare care au un buer de 1-caracter(octet) n care este citit n avans si caracterul (octetul) care urmeaz a celui curent citit. A sare PrintWriter PrintStream Ofer a metode convenabile pentru asarea informat iilor.

4.2.3

Crearea unui ux

Orice ux este un obiect al clasei ce implementeaz a uxul respectiv. Crearea unui ux se realizeaz a a sadar similar cu crearea obiectelor, prin instruct iunea new si invocarea unui constructor corespunz ator al clasei respective: Exemple:

102

S CAPITOLUL 4. INTRARI I IES IRI

//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"); A sadar, crearea unui ux primitiv de date care cite ste/scrie informat ii de la un dispozitiv extern are formatul general: FluxPrimitiv numeFlux = new FluxPrimitiv(dispozitivExtern); Fluxurile de procesare nu pot exista de sine st at atoare ci se suprapun pe 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 referint a la un ux primitiv responsabil cu citirea/scrierea efectiv a a datelor: 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); A sadar, crearea unui ux pentru procesarea datelor are formatul general:

4.2. FOLOSIREA FLUXURILOR

103

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

4.2.4

Fluxuri pentru lucrul cu siere

Fluxurile pentru lucrul cu siere sunt cele mai usor de nteles, ntruc at operat ia lor de baz a este citirea, respectiv scrierea unui caracter sau octet dintr-un sau ntr-un sier specicat uzual prin numele s au complet sau relativ la directorul curent. Dup a cum am v azut deja, clasele care implementeaz a aceste uxuri sunt urm atoarele: FileReader, FileWriter - caractere FileInputStream, FileOutputStream - octeti Constructorii acestor clase accept a ca argument un obiect care s a specice un anume sier. Acesta poate un sir de caractere, on obiect de tip File 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

S CAPITOLUL 4. INTRARI I IES IRI

Cei mai uzuali constructori sunt cei care primesc ca argument numele sierului. Ace stia pot provoca except ii de tipul FileNotFoundException n cazul n care sierul cu numele specicat nu exist a. Din acest motiv orice creare a unui ux de acest tip trebuie f acut a ntr-un bloc try-catch sau metoda n care sunt create uxurile respective trebuie s a arunce except iile de tipul FileNotFoundException sau de tipul superclasei IOException.

S a consider am ca exemplu un program care copie cont inutul unui sier cu numele in.txt ntr-un alt sier cu numele out.txt. Ambele siere sunt considerate n directorul curent. 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 n care vom lansa aplicat ia iar n directorul curent nu exist a un sier cu numele in.txt, va generat a o except ie de tipul FileNotFoundException. Aceasta va prins a de program deoarece, IOException este superclas a pentru FileNotFoundException. Dac a exist a sierul in.txt, aplicat ia va crea un nou sier out.txt n care va copiat cont inutul primului. Dac a exist a deja sierul out.txt el va re-

4.2. FOLOSIREA FLUXURILOR

105

scris. Dac a doream s a facem operat ia de ad augare(append) si nu de rescriere pentru sierul out.txt foloseam: FileWriter out = new FileWriter("out.txt", true);

4.2.5

Citirea si scrierea cu buer

Clasele pentru citirea/scrierea cu zona tampon sunt: BufferedReader, BufferedWriter - caractere BufferedInputStream, BufferedOutputStream - octeti Sunt folosite pentru a introduce un buer (zon a de memorie) n procesul de citire/scriere a informat iilor, reduc and astfel numarul de acces ari ale dispozitivului ce reprezint a sursa/destinat ia atelor. Din acest motiv, sunt mult mai eciente dec at uxurile f ar a buer si din acest motiv se recomand a folosirea lor ori de c ate ori este posibil. Clasa BufferedReader cite ste n avans date si le memoreaz a ntr-o zon a tampon. Atunci c and se execut a o operat ie de citire, caracterul va preluat din buer. In cazul n care buer-ul este gol, citirea se face direct din ux si, odat a cu citirea caracterului, vor memorati n buer si caracterele care i urmeaz a. Evident, BufferedInputStream funct ioneaz a dup a acela si principiu, singura diferent a ind faptul c a sunt citit i octet i. Similar lucreaza si clasele BufferedWriter si BufferedOutputStream. La operat iile de scriere datele scrise nu vor ajunge direct la destinat ie, ci vor memorate jntr-un buer de o anumit a dimensiune. Atunci c and buerul este plin, cont inutul acestuia va transferat automat la destinat ie. Fluxurile de citire/scriere cu buer sunt uxuri de procesare si sunt 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 folosit i ai acestor clase sunt urm atorii: BufferedReader(Reader in) BufferedReader(Reader in, int dim_buffer) BufferedWriter(Writer out)

106

S CAPITOLUL 4. INTRARI I IES IRI

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 n care dimensiunea buer-ului nu este specicat a, aceasta prime ste valoarea implicit a de 512 octet i (caractere). Metodele acestor clase sunt cele uzuale de tipul read si write. Pe l anga acestea, clasele pentru scriere prin buer mai au si metoda flush care gole ste explicit zona tampon, chiar dac a aceasta nu este plin 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 a uxurilor de citire cu buer si permite citirea linie cu linie a datelor de intrare. O linie reprezint a o succesiune de caractere terminat a cu simbolul pentru sf ar sit de linie, dependent de platforma de lucru. Acesta este reprezentat n Java prin secvent a escape \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 a combine serial mai multe uxuri de intrare astfel nc at acestea s a apar a ca un singur ux de intrare. Citirea datelor dintr-un astfel de ux se face astfel: se cite ste din primul ux de intrare specicat p ana c and se ajunge la sf arsitul acestuia, dup a care primul ux de intrare este nchis si se deschide automat urm atorul ux de intrare din care se vor citi n continuare datele, dup a care procesul se repet a p ana la terminarea tuturor uxurilor de intrare. Constructorii acestei clase sunt: SequenceInputStream(Enumeration e) SequenceInputStream(InputStream s1, InputStream s2) Primul construieste un ux secvential dintr-o mult ime de uxuri de intrare. Fiecare obiect n enumerarea primit a ca parametru trebuie s a e de tipul InputStream. Cel de-al doilea construie ste un ux de intrare care combin a doar dou a uxuri s1 si s2, primul ux citit ind s1. Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea a dou a sau mai multor siere: Listing 4.2: Concatenarea a dou a siere
/* 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 SequenceInputStream ( f1 , f2 ) ; int c ; while (( c = s . read () ) != -1) System . out . print (( char ) c ) ;

108

S CAPITOLUL 4. INTRARI I IES IRI


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

} }

Pentru concatenarea mai multor siere exist a dou a variante: folosirea unei enumer ari - primul constructor (vezi Colect ii); concatenarea pe r and a acestora folosind al 2-lea constructor; concatenarea a 3 siere va construi un ux de intrare astfel: 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 ata seaz a altui ux pentru a ltra datele care sunt citite/scrise de c atre acel ux. Clasele pentru ltrarea datelor superclasele abstracte: FilterInputStream - pentru ltrarea uxurilor de intrare si FilterOutputStream - pentru ltrarea uxurilor de ie sire. Cele mai importante uxruri pentru ltrarea datelor sunt implementate de clasele: DataInputStream, DataOutputStream BufferedInputStream, BufferedOutputStream LineNumberInputStream PushbackInputStream PrintStream

4.2. FOLOSIREA FLUXURILOR Observat i c a toate aceste clase descriu uxuri de octet i.

109

Filtrarea datelor nu trebuie v azut a ca o metod a de a elimina anumit i octeti dintr-un ux ci de a transforma ace sti octet i n date care s a poat a interpretate sub alt a form a. A sa cum am v azut la citirea/scrierea cu zon a tampon, clasele de ltrare BufferedInputStream si BufferedOutputStream colecteaz a datele unui ux ntr-un buer, urm and ca citirea/scrierea s a se fac a prin intermediu acelui buer. A sadar, uxurile de ltrare nu elimin a date citite sau scrise de un anumit ux, ci introduc o noua modalitate de manipulare a lor, ele mai ind numite si uxuri de procesare. Din acest motiv, uxurile de ltrare vor cont ine anumite metode specializate pentru citirea/scrierea datelor, altele dec at cele comune tuturor uxurilor. De exemplu, clasa BufferedInputStream pune la dispozit ie metoda readLine pentru citirea unei linii din uxul de intrare. Folosirea uxurilor de ltrare se face prin ata sarea lor de un ux care se ocup a efectiv de citirea/scrierea datelor: FluxFiltrare numeFlux = new FluxFiltrare(referintaAltFlux);

4.2.8

Clasele DataInputStream si DataOutputStream

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

110 DataInputStream readBoolean readByte readChar readDouble readFloat readInt readLong readShort readUTF

S CAPITOLUL 4. INTRARI I IES IRI DataOutputStream writeBoolean writeByte writeChar writeDouble writeFloat writeInt writeLong writeShort writeUTF

Aceste metode au denumirile generice de readXXX si writeXXX, specicate de interfetele DataInput si DataOutput si pot provoca except ii de tipul IOException. Denumirile lor sunt sugestive pentru tipul de date pe care l prelucreaz a. mai put in readUTF si writeUTF care se ocup a cu obiecte de tip String, ind singurul tip referint a permis de aceste clase. Scrierea datelor folosind uxuri de acest tip se face n format binar, ceea ce nseamn a c a un sier n care au fost scrise informat ii folosind metode writeXXX nu va putea citit dec at prin metode readXXX. Transformarea unei valori n format binar se nume ste serializare. Clasele DataInputStream si DataOutputStream permit serializarea tipurilor primitive si a sirurilor de caractere. Serializarea celorlalte tipuri referint a va f acut a prin intermediul altor clase, cum ar ObjectInputStream si ObjectOutputStream (vezi Serializarea obiectelor).

4.3

Intr ari si ie siri formatate

Incep and cu versiunea 1.5, limbajul Java pune la dispozit ii modalit a ti simplicate pentru a sarea formatat a a unor informat ii, respectiv pentru citirea de date formatate de la tastatur a.

4.3.1

Intr ari formatate

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

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

111

4.3.2

Ie siri formatate

Clasele PrintStream si PrintWriter pun la dispozit iile, pe l ang a metodele print, println care ofereau posibilitatea de a a sa un sir de caractere, si metodele format, printf (echivalente) ce permit a sarea formatat a a unor variabile. System.out.printf("%s %8.2f %2d %n", nume, salariu, varsta); Formatarea sirurilor de caractere se bazeaz a pe clasa java.util.Formatter.

4.4

Fluxuri standard de intrare si ie sire

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

112

S CAPITOLUL 4. INTRARI I IES IRI

4.4.1

Asarea informat iilor pe ecran

Am v azut deja numeroase exemple de utilizare a uxului standard de ie sire, el ind folosit la a sarea oric aror rezultate pe ecran ( n modul consola): System.out.print (argument); System.out.println(argument); System.out.printf (format, argumente...); System.out.format (format, argumente...); Fluxul standard pentru a sarea erorilor se folose ste similar si apare uzual n secvent ele de tratare a except iilor. Implicit, este acela si cu uxul standard de ie sire. catch(Exception e) { System.err.println("Exceptie:" + e); } Fluxurile de ie sire pot folosite a sadar f ar a probleme deoarece tipul lor este PrintStream, clas a concret a pentru scrierea datelor. In schimb, uxul standard de intrare System.out este de tip InputStream, care este o clas a abstract a, deci pentru a-l putea utiliza ecient va trebui sa-l folosim mpreuna cu un ux de procesare(ltrare) care s a permit a citirea facil a a datelor.

4.4.2

Citirea datelor de la tastatur a

Uzual, vom dori s a folosim metoda readLine pentru citirea datelor de la tastatura si din acest motiv vom folosi intrarea standard mpreun a cu o clas a de procesare care ofer a aceast a metod a. Exemplul tipic este: BufferedReader stdin = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Introduceti o linie:"); String linie = stdin.readLine() System.out.println(linie); In exemplul urm ator este prezentat un program care a seaza liniile introduse de la tastatur a p an a n momentul n care se introduce linia exit sau o linie vid a si ment ioneaz adac a sirul respectiv reprezint a un num ar sau nu.

4.4. FLUXURI STANDARD DE INTRARE S I IES IRE 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 matEx cepti on e ) { System . out . println ( " : NU " ) ; } } } catch ( IOException e ) { System . err . println ( " Eroare la intrarea standard ! " ) ; e . printStackTrace () ; } } }

113

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

4.4.3

Redirectarea uxurilor standard

Redirectarea uxurilor standard presupune stabilirea unei alte surse dec at tastatura pentru citirea datelor, respectiv alte destinat ii dec at ecranul pentru cele dou a uxuri de ie sire. In clasa System exist a urm atoarele metode statice care realizeaz a acest lucru: setIn(InputStream) - redirectare intrare setOut(PrintStream) - redirectare iesire setErr(PrintStream) - redirectare erori

114

S CAPITOLUL 4. INTRARI I IES IRI

Redirectarea ie sirii este util a n special atunci c and sunt a sate foarte multe date pe ecran. Putem redirecta asarea c atre un sier pe care s a-l citim dup a execut ia programului. Secvent a clasic a de redirectare a ie sirii este c atre un sier este: PrintStream fis = new PrintStream( new FileOutputStream("rezultate.txt"))); System.setOut(fis); Redirectarea erorilor ntr-un sier poate de asemenea util a si se face ntr-o manier a similar a: PrintStream fis = new PrintStream( new FileOutputStream("erori.txt"))); System.setErr(fis); Redirectarea intr arii poate folositoare pentru un program n mod consol a care prime ste mai multe valori de intrare. Pentru a nu le scrie de la tastatur a de ecare dat a n timpul test arii programului, ele pot puse ntrun sier, redirect and intrarea standard c atre acel sier. In momentul c and testarea programului a luat sf arsit redirectarea poate eliminat a, datele ind cerute din nou de la tastatur a. Listing 4.4: Exemplu de folosire a redirect arii:
import java . io .*; class Redirectare { public static void main ( String [] args ) { try { BufferedInputStream in = new BufferedInputStream ( 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 InputStreamReader ( System . in ) ) ;

4.4. FLUXURI STANDARD DE INTRARE S I IES IRE


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 a pe uxuri (clasa StreamTokenizer)

Clasa StreamTokenizer proceseaz a un ux de intrare de orice tip si l mparte n atomi lexicali. Rezultatul va consta n faptul c a n loc s a se citeasc a octet i sau caractere, se vor citi, pe r and, atomii lexicali ai uxului respectiv. Printr-un atom lexical se n]elege n general: un identicator (un sir care nu este ntre ghilimele) un numar un sir de caractere un comentariu un separator Atomii lexicali sunt desp art iti ntre ei de separatori. Implicit, ace sti separatori sunt cei obi snut i: spat iu, tab, virgul a, punct si virgula, etc., ns a pot schimbat i prin diverse metode ale clasei. Constructorii clasei sunt:

116

S CAPITOLUL 4. INTRARI I IES IRI

public StreamTokenizer(Reader r) public StreamTokenizer(InputStream is) Identicarea tipului si valorii unui atom lexical se face prin intermediul variabilelor: a sf aar situl uxului TT EOF - atom ce marcheaz TT EOL - atom ce marcheaz a sf ar situl unei linii TT NUMBER - atom de tip num ar TT WORD- atom de tip cuv ant ttype- tipul ultimului atom citit din ux nval- valoarea unui atom numeric sval - valoarea unui atom de tip cuv ant Citirea atomilor din ux se face cu metoda nextToken(), care returnez a tipul atomului lexical citit si scrie n variabilele nval sau sval valoarea corespunzato are atomului. Exemplul tipic de folosire a unui analizor lexical este citirea unei secvent e de numere si siruri aate ntr-un sier sau primite de la tastatur 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 (FIS IERE 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

A sadar, modul de utilizare tipic pentru un analizor lexical este ntr-o bucla while, n care se citesc atomii unul c ate unul cu metoda nextToken, p ana se ajunge la sf arsitul uxului (TT EOF). In cadrul buclei while se determin a tipul atomul curent curent ( ntors de metoda nextToken) si apoi se a a valoarea numeric a sau sirul de caractere corespunz ator atomului respectiv. In cazul n care tipul atomilor nu ne intereseaz a, este mai simplu s a citim uxul linie cu linie si s a folosim clasa StringTokenizer, care realizeaz a mp art irea unui sir de caractere n atomi lexicali, sau metoda split a clasei String.

4.5

Clasa RandomAccesFile ( siere cu acces direct)

Dup a cum am v azut, uxurile sunt procese secvent iale de intrare/ie sire. Acestea sunt adecvate pentru lucrul cu medii secvent iale de memorare a datelor, cum ar banda magnetic a sau pentru transmiterea informat iilor prin ret ea, desi sunt foarte utile si pentru dispozitive n care informat ia poate accesat a direct. Clasa RandomAccesFile are urm atoarele caracteristici: permite accesul nesecvent ial (direct) la cont inutul unui sier; este o clas a de sine st at atoare, subclas a direct a a clasei Object; se g ase ste n pachetul java.io;

118

S CAPITOLUL 4. INTRARI I IES IRI

implementeaz a interfet ele DataInput si DataOutput, ceea ce nseamna ca sunt disponibile metode de tipul readXXX, writeXXX, ntocmai ca la clasele DataInputStream si DataOutputStream; permite at at citirea c at si scriere din/in siere cu acces direct; permite specicarea modului de acces al unui sier (read-only, readwrite). Constructorii acestei clase sunt: RandomAccessFile(StringnumeFisier, StringmodAcces) throws IOException RandomAccessFile(StringnumeFisier, StringmodAcces) throws IOException unde modAcces poate : r - sierul este deschis numai pentru citire (read-only) rw - sierul este deschis pentru citire si scriere (read-write) 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 a not iunea de pointer de sier. Acesta este un indicator ce specic a pozit ia curent a n sier. La deschiderea unui sier pointerul are valoarea 0, indic and nceputul sierului. Apeluri la metodele de citire/scrirere deplaseaz a pointerul sierului cu num arul de octet i citit i sau scri si de metodele respective. In plus fat ade metodele de tip read si write clasa pune la dispozitie si metode pentru controlul pozit iei pointerului de sier. Acestea sunt: skipBytes - mut a pointerul sierului nainte cu un num ar specicat de octet i seek - pozit ioneaza pointerului sierului naintea unui octet specicat getFilePointer - returneaz a pozit ia pointerului de sier.

4.6. CLASA FILE

119

4.6

Clasa File

Clasa File nu se refer a doar la un sier ci poate reprezenta e un sier anume, e multimea sierelor dintr-un director. Specicarea unui sier/director se face prin specicarea c aii absolute spre acel sier sau a c aii relative fat a de directorul curent. Acestea trebuie s a respecte convent iile de specicare a c ailor si numelor sierelor de pe platforma de lucru. Utilitate clasei File const a n furnizarea unei modalit a ti de a abstractiza dependent ele cailor si numelor sierelor fat ade ma sina gazd a, precum si punerea la dispozit ie a unor metode pentru lucrul cu sere si directoare la nivelul sistemului de operare. Astfel, n aceast a clas a vom g asi metode pentru testarea existent ei, stergerea, redenumirea unui sier sau director, crearea unui director, listarea sierelor dintr-un director, etc. Trebuie ment ionat si faptul c a majoritatea constructorilor uxurilor care permit accesul la siere accept a ca argument un obiect de tip File n locul unui sir ce reprezint a numele sierului respectiv. 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 si vor prezentate prin intermediul exemplului urm ator care listeaz a sierele si subdirectoarele unui director specicat si, pentru ecare din ele a seaz a diverse informat ii: Listing 4.6: Listarea cont inutului 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 {

S CAPITOLUL 4. INTRARI I IES IRI

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 Interfet e
5.1
5.1.1

Introducere
Ce este o interfat a?

Interfet ele duc conceptul de clas a abstract a cu un pas nainte prin eliminarea oric aror implement ari de metode, pun and n practic a unul din conceptele program arii orientate obiect si anume cel de separare a modelului unui obiect (interfat a) de implementarea sa. A sadar, o interfat a poate privita ca un protocol de comunicare ntre obiecte. O interfat a Java dene ste un set de metode dar nu specic a nici o implementare pentru ele. O clas a care implementeaz a o interfat a trebuie obligatoriu s a specice implement ari pentru toate metodele interfet ei, supun andu-se a sadar unui anumit comportament. Denit ie O interfat a este o colect ie de metode f ar a implementare si declarat ii de constante. Interfet ele permit, al aturi de clase, denirea unor noi tipuri de date. 121

122

CAPITOLUL 5. INTERFET E

5.2
5.2.1

Folosirea interfet elor


Denirea unei interfet e

Denirea unei interfet e se face prin intermediul cuv antului cheie interface: [public] interface NumeInterfata [extends SuperInterfata1, SuperInterfata2...] { /* Corpul interfetei: Declaratii de constane Declaratii de metode abstracte */ } O interfat a poate avea un singur modicator si anume public. O interfat a public a este accesibil a tuturor claselor, indiferent de pachetul din care fac parte, implicit nivelul de acces ind doar la nivelul pachetului din care face parte interfat a. O interfat a poate extinde oric ate interfet e. Acestea se numesc superinterfet e si sunt separate prin virgul a. (vezi Mo stenirea multipl a prin intermediul interfet elor). Corpul unei interfet e poate cont ine: constante: acestea pot sau nu declarate cu modicatorii public, static si final care sunt implicit i, nici un alt modicator neput and ap area n declarat ia unei variabile dintr-o interfat a. Constantele unei interfet e trebuie obligatoriu init ializate. 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 INTERFET ELOR

123

metode f ar a implementare: acestea pot sau nu declarate cu modicatorul public, care este implicit; nici un alt modicator nu poate ap area n declarat ia unei metode a unei interfet e. interface Exemplu { void metoda(); // Echivalent cu: public void metoda(); protected void metoda2(); // Incorect, modificator nepermis

Atent ie Variabilele unei interfet e sunt implicit publice chiar dac a nu sunt declarate cu modicatorul public. Variabilele unei interfet e sunt implicit constante chiar dac a nu sunt declarate cu modicatorii static si final. Metodele unei interfet e sunt implicit publice chiar dac a nu sunt declarate cu modicatorul public. In variantele mai vechi de Java era permis si modicatorul abstract n declaratia interfet ei si n declarat iile metodelor, ns a acest lucru nu mai este valabil, deoarece at at interfat a c at si metodele sale nu pot altfel dec at abstracte.

5.2.2

Implementarea unei interfet e

Implementarea uneia sau mai multor interfet e de c atre o clas a se face prin intermediul cuv antului cheie implements: class NumeClasa implements NumeInterfata sau class NumeClasa implements Interfata1, Interfata2, ...

124

CAPITOLUL 5. INTERFET E

O clas a poate implementa oric ate interfet e sau poate s a nu implementeze nici una. In cazul n care o clas a implementeaz a o anumit a interfat a, atunci trebuie obligatoriu s a specice cod pentru toate metodele interfet ei. Din acest motiv, odat a creata si folosit a la implementarea unor clase, o interfat a nu mai trebuie modicat a, n sensul c a ad augarea unor metode noi sau schimbarea signaturii metodelor existente vor duce la erori n compilarea claselor care o implementeaz a. Evident, o clas a poate avea si alte metode si variabile membre n afar a de cele denite n interfat a.

Atent ie Modicarea unei interfet e implic a modicarea tuturor claselor care implementeaz a acea interfat a.

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

5.2.3

Exemplu: implementarea unei stive

S a consider am urm atorul exemplu. Dorim s a implement am un nou tip de date numit Stack, care s a modeleze not iunea de stiv a de obiecte. Obiectele de tip stiv a, indiferent de implementarea lor, vor trebui s a cont in a metodele: push - adaug a un nou element in stv a pop - elimin a elementul din v arful stivei peek - returneaz a varful stivei empty - testeaz a dac a stiva este vid a toString - returneaz a cont inutul stivei sub forma unui sir de caractere.

5.2. FOLOSIREA INTERFET ELOR

125

Din punctul de vedere al structurii interne, o stiv a poate implementat a folosind un vector sau o list a nl ant uit a, ambele solut ii av and avantaje si dezavantaje. Prima solut ie este mai simplu de nt eles, n timp ce a doua este mai ecient a din punctul de vedere al folosirii memoriei. Deoarece nu dorim s a leg am tipul de date Stack de o anumit a implementare structural a, l vom deni prin intermediul unei interfet e. Vom vedea imediat avantajele acestei abord ari. Listing 5.1: Interfat a 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 situat iile anormale care pot ap area atunci c and ncerc am s a punem un element n stiv a si nu este posibil din lips a de memorie, sau ncerc am s a acces am v arful stivei si aceasta este vid a, vom deni o except ie proprie StackException: Listing 5.2: Tipul de except ie generat de stiv a
public class StackException extends Exception { public StackException () { super () ; } public StackException ( String msg ) { super ( msg ) ; } }

D am n continuare prima implementare a stivei, folosind un vector: 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. INTERFET E

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 ; } }

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

5.2. FOLOSIREA INTERFET ELOR

127

O alt a observat ie important a se refer a la faptul c a trebuie s a declar am n cadrul interfet ei si except iile aruncate de metode, ce trebuie obligatoriu tratate. S a vedem acum modalitatea de implementare a stivei folosind o list a nl ant uit 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. INTERFET E
s += ( node . item ) . toString () + " " ; node = node . link ; } return s ; }

Singura observat ie pe care o facem aici este c a, de si metoda push din interfat a declar a aruncarea unor except ii de tipul StackException, nu este obligatoriu ca metoda din clas a s a specice si ea acest lucru, at at timp c at nu genereaz a except ii de acel tip. Invers este ns a obligatoriu. In continuare este prezentat a o mic a aplicat ie demonstrativ a care folose ste tipul de date nou creat si cele dou a implement ari ale sale: 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 () ; } } }

Observat i folosirea interfet ei Stack ca un tip de date, ce aduce exibilitate sporit a n manevrarea claselor ce implementeaz a tipul respectiv. Metoda

5.3. INTERFET E S I CLASE ABSTRACTE

129

afiseaza accept a ca argument orice obiect al unei clase ce implementeaz a Stack. Observat ie In pachetul java.util exist a clasa Stack care modeleaz a not iune de stiv a de obiecte si, evident, aceasta va folosit a n aplicat iile ce au nevoie de acest tip de date. Exemplu oferit de noi nu are leg atur a cu aceast a clas a si are rol pur demonstrativ.

5.3

Interfet e si clase abstracte

La prima vedere o interfat a nu este altceva dec at o clas a abstract a n care toate metodele sunt abstracte (nu au nici o implementare). A sadar, o clas a abstract a nu ar putea nlocui o interfat a? Raspunsul la intrebare depinde de situat ie, ns a n general este Nu. Deosebirea const a n faptul c a unele clase sunt fort ate s a extind a o anumit a clas a (de exemplu orice applet trebuie s a e subclasa a clasei Applet) si nu ar mai putea sa extind a o alt a clas a, deoarece n Java nu exista dec at mo stenire simpla. Fara folosirea interfet elor nu am putea fort a clasa respectiv a s a respecte diverse tipuri de protocoale. La nivel conceptual, diferent a const a n: extinderea unei clase abstracte fort eaz a o relat ie ntre clase; implementarea unei interfet e specic a doar necesitatea implement arii unor anumie metode. In multe situat ii interfet ele si clasele abstracte sunt folosite mpreun a pentru a implementa c at mai exibil si ecient o anumit a ierarhie de clase. Un exemplu sugestiv este dat de clasele ce descriu colect ii. Ca sa particulariz am, exist a: interfat a List care impune protocolul pe care trebuie s a l respecte o clas a de tip list a, clasa abstract a AbstractList care implementeaz a interfat a List si ofer a implement ari concrete pentru metodele comune tuturor tipurilor de list a,

130

CAPITOLUL 5. INTERFET E

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

5.4

Mo stenire multipl a prin interfet e

Interfet ele nu au nici o implementare si nu pot instant iate. Din acest motiv, nu reprezint a nici o problem a ca anumite clase s a implementeze mai multe interfet e sau ca o interfat a s a extind a mai multe interfet e (s a aib a mai multe superinterfet e) class NumeClasa implements Interfata1, Interfata2, ... interface NumeInterfata extends Interfata1, Interfata2, ... O interfat a mosteneste at at constantele c at si declarat iile de metode de la superinterfet ele sale. O clas a mo steneste doar constantele unei interfet e si responsabilitatea implement arii metodelor sale. S a consider am un exemplu de clasa care implementeaza mai multe interfet e: 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 a care extinde mai multe interfet e: interface Monstru { void ameninta(); } interface MonstruPericulos extends Monstru { void distruge();

PRIN INTERFET 5.4. MOS TENIRE MULTIPLA E } 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 ap area situat ii de ambiguitate, atunci c and exist a constante sau metode cu acelea si nume n mai multe interfet e, ns a acest lucru trebuie ntotdeauna evitat, deoarece scrierea unui cod care poate confuz este un stil prost de programare. In cazul in care acest lucru se nt ampl a, compilatorul nu va furniza eroare dec at dac a se ncearc a referirea constantelor ambigue f ar a a le prexa cu numele interfet ei sau dac a metodele cu acela si nume nu pot deosbite, cum ar situat ia c and au aceea si list a de argumente dar 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. INTERFET E

S a recapitul am c ateva lucruri legate de clase si interfet e: O clas a nu poate avea dec at o superclas a. O clas a poate implementa oric ate interfet e. O clas a trebuie obligatoriu s a trateze metodele din interfet ele pe care la implementeaz a. Ierarhia interfet elor este independent a de ierarhia claselor care le implementeaz a.

5.5

Utilitatea interfet elor

Dup a cum am v azut, o interfat a dene ste un protocol ce poate implementat de orice clas a, indiferent de ierarhia de clase din care face parte. Interfet ele sunt utile pentru: denirea unor similaritati ntre clase independente f ar a a fort a articial o legatur a ntre ele; asigur a c a toate clasele care implementeaz a o interfat a pun la dipozit ie metodele specicate n interfat a - de aici rezult a posibilitatea implement arii unor clase prin mai multe modalit a ti si folosirea lor ntr-o manier a unitar a; denirea unor grupuri de constante; transmiterea metodelor ca parametri;

5.5.1

Crearea grupurilor de constante

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

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

133

5.5.2

Transmiterea metodelor ca parametri

Deoarece nu exist a pointeri propriu-zi si, transmiterea metodelor ca parametri este realizat a n Java prin intermediul interfet elor. Atunci c and o metod a trebuie s a primeasc a ca argument de intrare o referint a la o alt a funct ie necesar a execut iei sale, cunoscut a doar la momentul execut iei, atunci argumentul respectiv va declarat de tipul unei interfet e care cont ine metoda respectiv a. La execut ie metoda va putea primi ca parametru orice obiect ce implementeaz a interfat a respectiv a si deci cont ine si codul funct iei respective, aceasta urm and s a e apelat a normal pentru a obt ine rezultatul dorit. Aceast a tehnic a, denumit a si call-back, este extrem de folosit a n Java si trebuie neap arat nt eleas a. S a consider am mai multe exemple pentru a clarica lucrurile. Primul exemplu se refer a la explorarea nodurilor unui graf. In ecare nod trebuie s a se execute prelucrarea informat iei din nodul respectiv prin intermediul unei funct ii primite ca parametru. Pentru aceasta, vom deni o interfat a Functie care va specica metoda trimis a ca parametru. interface Functie { public void executa(Nod u); } class Graf { //... void explorare(Functie f) { //...

134

CAPITOLUL 5. INTERFET E 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 n sect iunea urm atoare, ntruc at face parte din API-ul standard Java si vor puse n evident a, prin intermediul s au, si alte tehnici de programare.

5.6

Interfat a FilenameFilter

Instant ele claselor ce implementeaz a aceasta interfat a sunt folosite pentru a crea ltre pentru siere si sunt primite ca argumente de metode care listeaz a cont inutul unui director, cum ar metoda list a clasei File. A sadar, putem spune c a metoda list prime ste ca argument o alt a funct ie care specic a dac a un sier va returnat sau nu (criteriul de ltrare).

5.6. INTERFAT A FILENAMEFILTER

135

Interfat a FilenameFilter are o singur a metod a: accept care specic a criteriul de ltrare si anume, testeaz a dac a numele sierului primit ca parametru ndepline ste condit iile dorite de noi. Denit ia interfet ei este: public interface FilenameFilter { public boolean accept(File dir, String numeFisier); } A sadar, orice clas a de specicare a unui ltru care implementez a interfat a FilenameFilter trebuie s a implementeze metoda accept a acestei interfet e. Aceste clase mai pot avea si alte metode, de exemplu un constructor care s a primeasca criteriul de ltrare. In general, o clas a de specicare a unui ltru are urm atorul format: 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 sierelor sunt: endsWith - testeaz a dac a un sir are o anumit a terminat ie indexOf - testeaz a dac a un sir cont ine un anumit sub sir, return and pozit ia acestuia, sau 0 n caz contrar.

136

CAPITOLUL 5. INTERFET E

Instant ele claselor pentru ltrare sunt primite ca argumente de metode de listare a cont inutului unui director. O astfel de metod a este list din clasa File: String[] list (FilenameFilter filtru) Observati c a aici interfat a este folosit a ca un tip de date, ea ind substituit a cu orice clas a care o implementeaz a. Acesta este un exemplu tipic de transmitere a unei funct ii (funct ia de ltrare accept) ca argument al unei metode. S a consider am exemplul complet n care dorim s a list am sierele din directorul curent care au o anumit a extensie. Listing 5.6: Listarea sierelor cu o anumit a extensie
/* 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. INTERFAT A FILENAMEFILTER


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

137

5.6.1

Folosirea claselor anonime

In cazul n care nu avem nevoie de clasa care reprezint a ltrul pentru listarea sierelor dintr-un director dec at o singur a dat a, pentru a evita crearea unei noi clase de sine st at atoare care s a e folosit a pentru instant ierea unui singur obiect, putem folosi clas a intern a anonim a, aceast a situat ie ind un exemplu 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. INTERFET E

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

5.7

Compararea obiectelor

Am v azut n primul capitol c a o solut ie facil a si ecient a de sortare a unui 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 n care elementele din vector sunt de tip primitiv, ca in exemplul de mai sus, nu exist a nici o problem a n a determina ordinea reasc a a elementelor. Ce se nt ampl a ns a atunci c and vectorul cont ine referint e la obiecte de un anumit tip ? S a consider am urm atorul exemplu, n care dorim s a sort am un vector format din instant e ale clasei Persoana, denit a mai jos: Listing 5.8: Clasa Persoana (f ar a suport pentru comparare)
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 urm ator ar trebui s a sorteze un vector de persoane: 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 execut ia acestei aplicat ii va obt inut a o except ie, deoarece metoda sort nu stie care este ordinea natural a a obiectelor de tip Persoana. Va trebui, ntr-un fel sau altul, s a specic am acest lucru.

5.7.1

Interfat a Comparable

Interfat a Comparable impune o ordine total a asupra obiectelor unei clase ce o implementeaz a. Aceast a ordine se nume ste ordinea natural a a clasei si este specicat a prin intermediul metodei compareTo. Denit ia interfet ei este: public interface Comparable { int compareTo(Object o); } A sadar, o clas a ale c arei instant e trebuie s a e comparabil va implementa metoda compareTo care trebuie s a returneze: o valoare strict negativ a: dac a obiectul curent (this) este mai mic dec a obiectul primit ca argument; zero: dac a obiectul curent este egal dec a obiectul primit ca argument;

140

CAPITOLUL 5. INTERFET E

o valoare strict pozitiv a: dac a obiectul curent este mai mare dec a obiectul primit ca argument. Reamintim c a metoda equals, mo stenit a din Object de toate clasele, determin a dac a dou a obiecte sunt egale (au aceea si valoare). Spunem c a ordinea natural a a unei clase C este consitent a cu equals dac a si numai dac a (e1.compareTo((Object)e2) == 0) are aceeas si valoare logic a cu e1.equals((Object)e2, pentru orice e1, e2 instant e ale lui C . null nu este instant a a nici unei clase si e.compareTo(null) trebuie s a arunce o except ie de tip NullPointerException chiar dac a e.equals(null) returneaz a false. S a presupunem c a dorim ca ordinea natural a a persoanelor s a e dup 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 NullPointerException () ; if (!( o instanceof Persoana ) ) throw new ClassCastException ( " Nu pot compara ! " ) ; Persoana p = ( Persoana ) o ;

5.7. COMPARAREA OBIECTELOR


return ( cod - p . cod ) ; } }

141

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

5.7.2

Interfat a Comparator

In cazul n care dorim s a sort am elementele unui vector ce cont ine referint e dup a alt criteriu dec at ordinea natural a a elemenetelor, avem nevoie de o alt a solut ie. Aceasta este oferit a tot de metoda sort din clasa java.util.Arrays, dar n varianta n care, pe l ang a vectorul ce trebuie sortat, vom transmite un argument de tip Comparator care s a specice modalitatea de comparare a elementelor. Interfat a java.util.Comparator cont ine metoda compare, care impune o ordine total a asupra elementelor unei colect ii. Aceasta returneaz a un ntreg cu aceea si semnicat ie ca la metoda compareTo a interfet ei Comparator si are urm atoarea denit ie: int compare(Object o1, Object o2); S a presupunem c a dorim s a sort am persoanele ordonate dup a numele lor. Pentru denirea comparatorului vom folosi o clas a anonim 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. INTERFET E
} }) ; System . out . println ( " Persoanele ordonate dupa nume : " ) ; for ( int i =0; i < p . length ; i ++) System . out . println ( p [ i ]) ;

} }

Observat i cum compararea a dou a siruri de caractere se face tot cu metoda compareTo, clasa String implemen and interfat a Comparable.

5.8

Adaptori

In cazul n care o interfat a cont ine mai multe metode si, la un moment dat, avem nevoie de un obiect care implementeaz a interfat a respectiv dar nu specic a cod dec at pentru o singur a metod a, el trebui totu si s a implementeze toate metodele interfet ei, chiar dac a nu specic a nici un cod. 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 a abordare poate nepl acut a dac a avem frecvent nevoie de obiecte ale unor clase ce implementeaz a interfat a X . Solut ia la aceast a problem a este folosirea adaptorilor. Denit ie Un adaptor este o clas a abstract a care implementeaz a o anumit a interfat a f ar a a specica cod nici unei metode a interfet ei.

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

144

CAPITOLUL 5. INTERFET E

Capitolul 6 Organizarea claselor


6.1 Pachete

Denit ie Un pachet este o colect ie de clase si interfet e nrudite din punctul de vedere al funct ionalit a tii lor. Sunt folosite pentru g asirea si utilizarea mai u soar a a claselor, pentru a evita conictele de nume si pentru a controla accesul la anumite clase. In alte limbaje de programare pachetele se mai numesc libr arii sau bibilioteci.

6.1.1

Pachetele standard (J2SDK)

Platforma standard de lucru Java se bazeaz a pe o serie de pachete cu ajutorul c arora se pot construi ntr-o manier a simplicat a aplicat iile. Exist a deci un set de clase deja implementate care modeleaz a structuri de date, algoritmi sau diverse not iuni esent iale n dezvoltarea unui program. Cele mai importante pachete si suportul oferit de lor sunt: java.lang - clasele de baz a ale limbajului Java java.io - intr ari/ie siri, lucrul cu siere java.util - clase si interfet e utile java.applet - dezvoltarea de appleturi 145

146

CAPITOLUL 6. ORGANIZAREA CLASELOR

java.awt - interfat a grac a cu utilizatorul java.awt.event - mecanismele de tratare e evenimentelor generate de utilizator java.beans - scrierea de componente reutilizabile java.net - programare de ret ea java.sql - lucrul cu baze de date java.rmi - execut ie la distant a Remote Message Interface java.security - mecanisme de securitate: criptare, autenticare java.math - operat ii matematice cu numere mari java.text - lucrul cu texte, date si numere independent de limb a java.lang.reect - introspect ie javax.swing - interfat a grac a cu utilizatorul, mult mbog a tit a fat a de AWT. ...

6.1.2

Folosirea membrilor unui pachet

Conform specicat iilor de acces ale unei clase si ale mebrilor ei, doar clasele publice si membrii declarat i publici ai unei clase sunt accesibili n afara pachetului n care se g asesc. Dup a cum am v azut n sect iunea Specicatori de acces pentru membrii unei clase, accesul implicit n Java este la nivel de pachet. Pentru a folosi o clas a public a dintr-un anumit pachet, sau pentru a apela o metod a public a a unei clase publice a unui pachet, exist a trei solut ii: specicarea numelui complet al clasei importul clasei respective importul ntregului pachet n care se g ase ste clasa.

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 a metod a este recomandat a doar pentru cazul n care folosirea acelei clase se face o singur a dat a sau foarte rar. De exemplu, ar extrem de nepl acut s a scriem de ecare dat a c and vrem s a declar am un obiect grac secvent e de genul: 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 situat ii, vom importa n aplicat ia noastr a clasa respectiv a, sau ntreg pachetul din care face parte. Acest lucru se realizeaz a prin instruct iunea import, care trebuie s a apar a la nceputul sierelor surs a, nainte de declararea vreunei clase sau interfet e.

6.1.3

Importul unei clase sau interfet e

Se face prin instruct iunea import n care specic am numele complet al clasei sau interfet ei pe care dorim s a o folosim dintr-un anumit pacehet: import numePachet.numeClasa; //Pentru exemplul nostru: import java.awt.Button; import java.awt.TextField; Din acest moment, vom putea folosi n clasele sierului n care am plasat instruct iunea de import numele scurt al claselor Button si TextField: 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 a abordare este ecient a si recomandat a n cazul n care nu avem nevoie dec at de c ateva clase din pachetul respectiv. Dac a n exemplul nostru am avea nevoie si de clasele Line, Point, Rectangle, Polygon, ar trebui s a avem c ate o instruct iune de import pentru ecare dintre ele: 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 a situat ie ar mai simplu s a folosim importul la cerere din ntregul pachet si nu al ec arei clase n parte.

6.1.4

Importul la cerere dintr-un pachet

Importul la cerere dintr-un anumit pachet se face printr-o instruct iune import n care specic am numele pachetului ale c arui clase si interfet e dorim s a le folosim, urmat de simbolul *. Se nume ste import la cerere deoarece nc arcarea claselor se face dinamic, n momentul apel arii lor. import numePachet.*; //Pentru exemplul nostru: import java.awt.*; Din acest moment, vom putea folosi n clasele sierului n care am plasat instruct iunea de import numele scurt al tuturor claselor pachetului importat: Button b = new Button("OK"); Point p = new Point(0, 0);

Atent ie * nu are semnicat ia uzual a de la siere de wildcard (masc a) si nu poate folosit dec at ca atare. O expresie de genul import java.awt.C*; va produce o eroare de compilare.

6.1. PACHETE

149

In cazul n care sunt importate dou a sau mai multe pachete care cont in clase (interfet e) cu acela si nume, atunci referirea la ele trebuie f acut a doar folosind numele complet, n caz contrar ind semnalat a o ambiguitate de c atre compilator. 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 sier surs a, urm atoarele pachete: pachetul java.lang import java.lang.*; // Poate sau nu sa apara // Mai bine nu... pachetul curent pachetul implicit (f ar a nume)

6.1.5

Importul static

Aceast a facilitate, introdus a ncep and cu versiunea 1.5, permite referirea constantelor statice ale unei clase f ar a a mai specica numele complet al acesteia si este implementat a prin ad augarea cuv antului cheie static dup a cel de import: import static numePachet.NumeClasa.*; Astfel, n loc s a ne referim la constantele clasei cu expresii de tipul 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);

Atent ie Importul static nu import a dec at constantele statice ale unei clase, nu si clasa n sine.

6.1.6

Crearea unui pachet

Toate clasele si interfet ele Java apartin la diverse pachete, grupate dup a funct ionalitatea lor. Dup a cum am v azut clasele de baz a se g asesc n pachetul java.lang, clasele pentru intr ari/ie siri sunt n java.io, clasele pentru interfat a grac a n java.awt, etc. Crearea unui pachet se realizeaz a prin scriere la nceputul sierelor surs a ce cont in clasele si interfet ele pe care dorim s a le grup am ntr-un pachet a instruct iunii: package numePachet; S a consider am un exemplu: presupunem c a avem dou a siere surs a Graf.java si Arbore.java. //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 acela si pachet grafuri. Instruct iunea package act ioneaz a asupra ntregului sier surs a la nceputul c aruia apare. Cu alte cuvinte nu putem specica faptul c a anumite clase dintr-un sier surs a apart in unui pachet, iar altele altui pachet. Dac a nu este specicat un anumit pachet, clasele unui sier surs a vor face parte din pachetul implicit (care nu are nici un nume). In general, pachetul implicit este format din toate clasele si intefet ele directorului curent de lucru. Este recomandat ns a ca toate clasele si intefetele s a e plasate n pachete, pachetul implicit ind folosit doar pentru aplicat ii mici sau prototipuri.

6.1.7

Denumirea unui pachet

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

152

CAPITOLUL 6. ORGANIZAREA CLASELOR

6.2
6.2.1

Organizarea sierelor
Organizarea sierelor surs a

Orice aplicat ie nebanal a trebuie s a e construit a folosind o organizare ierarhic a a componentelor sale. Este recomandat ca strategia de organizare a sierelor surs a s a respecte urm atoarele convent ii: Codul surs a al claselor si interfet elor s a se gaseasc a n siere ale c aror nume s a e chiar numele lor scurt si care s a aib a extensia .java.

Atent ie Este obligatoriu ca o clas a/interfat a public a s a se gaseasc a ntr-un sier av and numele clasei(interfet ei) si extenisa .java, sau compilatorul va furniza o eroare. Din acest motiv, ntr-un sier surs a nu pot exista dou a clase sau interfet e publice. Pentru clasele care nu sunt publice acest lucru nu este obligatoriu, ci doar recomandat. Intr-un sier surs a pot exista oric ate clase sau interfet e care nu sunt publice.

Fi sierele surs a trebuie s a se g aseasc a n directoare care s a reecte numele pachetelor n care se g asesc clasele si interfet ele din acele siere. Cu alte cuvinte, un director va cont ine surse pentru clase si interfet e din acela si pachet iar numele directorului va chiar numele pachetului. Dac a numele pachetelor sunt formate din mai multe unit a ti lexicale separate prin punct, atunci acestea trebuie de asemenea s a corespund a unor directoare ce vor descrie calea spre sierele surs a ale c aror clase si interfet e fac parte din pachetele respective. Vom clarica modalitatea de organizare a sierelor surs a ale unei aplicatii printr-un exemplu concret. S a presupunem c a dorim crearea unor componente care s a reprezinte diverse not iuni matematice din domenii diferite, cum ar geometrie, algebr a, analiz a, etc. Pentru a simplica lucrurile, s a presupunem c a dorim s a cre am clase care s a descrie urm atoarele notiuni: poligon, cerc, poliedru, sfer a, grup, funct ie. O prim a variant a ar s a construim c ate o clas a pentru ecare si s a le plas am

6.2. ORGANIZAREA FIS IERELOR

153

n acela si director mpreuna cu un program care s a le foloseasca, ns a, av and n vedere posibila extindere a aplicat iei cu noi reprezent ari de not iuni matematice, aceast a abordare ar inecient a. O abordare elegant a ar aceea n care clasele care descriu not iuni din acela si domeniu sa se gaseasca n pachete separate si directoare separate. Ierarhia sierelor sursa ar : /matematica /surse /geometrie /plan Poligon.java Cerc.java /spatiu Poliedru.java Sfera.java /algebra Grup.java /analiza Functie.java Matematica.java Clasele descrise n sierele de mai sus trebuie declarate n pachete denumite corespunzator cu numele directoarelor n care se gasesc: // 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 a aplicat iei. Dup a cum se observ a, numele lung al unei clase trebuie s a descrie calea spre acea clas a n cadrul sierelor surs a, relativ la directorul n care se g ase ste aplicat ia.

6.2.2

Organizarea unit a tilor de compilare (.class)

In urma compil arii sierelor surs a vor generate unit a ti de compilare pentru ecare clas a si interfat a din sierele surs a. Dup a cum stim acestea au extensia .class si numele scurt al clasei sau interfet ei respective. Spre deosebire de organizarea surselor, un sier .class trebuie s a se gaseasca ntr-o ierarhie de directoare care s a reecte numele pachetului din care face parte clasa respectiv a. Implicit, n urma compil arii sierele surs a si unit a tile de compilare se g asesc n acela si director, ns a ele pot apoi organizate separat. Este recomandat ns a ca aceast a separare s a e f acut a automat la compilare. Revenind la exemplul de mai sus, vom avea urm atoarea organizare: /matematica /clase /geometrie /plan Poligon.class Cerc.class /spatiu

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

155

Crearea acestei structuri ierarhice este facut a automat de c atre compilator. In directorul aplicatiei (matematica) cre am subdirectorul clase si d am comanda: javac -sourcepath surse surse/Matematica.java -d clase sau javac -classpath surse surse/Matematica.java -d clase Opt iunea -d specic a directorul r ad acin a al ierarhiei de clase. In lipsa lui, ecare unitate de compilare va plasat a n acela si director cu sierul s au surs a. Deoarece compil am clasa principal a a plicat iei, vor compilate n cascad a toate clasele referite de aceasta, dar numai acestea. In cazul n care dorim s a compil am explicit toate sierele java dintr-un anumit director, de exemplu surse/geometrie/plan, putem folosi expresia: javac surse/geometrie/plan/*.java -d clase

6.2.3

Necesitatea organiz arii sierelor

Organizarea sierelor surs a este necesar a deoarece n momentul c and compilatorul nt alneste un nume de clas a el trebuie s a poat a identica acea clas a, ceea ce nseamna c a trebuie s a gaseasc a serul surs a care o cont ine. Similar, unit a tile de compilare sunt organizate astfel pentru a da posibilitatea interpretorului s a gaseasc a si s a ncarce n memorie o anumit a clas a n timpul execut iei programului. Ins a aceast a organizare nu este sucient a deoarece specic a numai partea nal a din calea c atre sierele .java si .class, de exemplu /matematica/clase/geometrie/plan/Poligon.class. Pentru aceasta, at at la compilare c at si la interpretare trebuie specicat a lista de directoare r ad acin a

156

CAPITOLUL 6. ORGANIZAREA CLASELOR

n care se g asesc sierele aplicat iei. Aceast a list a se nume ste cale de cautare (classpath). Denit ie O cale de c autare este o list a de directoare sau arhive n care vor c autate sierele necesare unei aplicat ii. Fiecare director din calea de cautare este directorul imediat superior structurii de directoare corespunz atoare numelor pachetelor n care se g asesc clasele din directorul respectiv, astfel nc at compilatorul si interpretorul s a poat a construi calea complet a spre clasele aplicat iei. Implicit, calea de c autare este format a doar din directorul curent. S a consider am clasa principal a a aplicat iei Matematica.java: 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 n program se face n felul urm ator: La directoarele aate n calea de c autare se adaug a subdirectoarele specicate n import sau n numele lung al clasei In directoarele formate este c autat un sier cu numele clasei. In cazul n care nu este g asit nici unul sau sunt g asite mai multe va semnalat a o eroare.

6.2.4

Setarea c aii de c autare (CLASSPATH)

Setarea c aii de c autare se poate face n dou a modalit a ti: Setarea variabilei de mediu CLASSPATH - folosind aceast a variant a toate aplicat iile Java de pe ma sina respectiv a vor c auta clasele necesare n directoarele specicate n variabila CLASSPATH.

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

157

Folosirea opt iunii -classpath la compilarea si interpretarea programelor - 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 n execut ie a aplicatiei noastre, din directorul matematica, se va face astfel: java -classpath clase Matematica In concluzie, o organizare ecient a a sierelor aplicat iei ar ar ata astfel: /matematica /surse /clase compile.bat (javac -sourcepath surse surse/Matematica.java -d clase) run.bat (java -classpath clase Matematica)

6.3

Arhive JAR

Fi sierele JAR (Java Archive) sunt arhive n format ZIP folosite pentru mpachetarea aplicat iilor Java. Ele pot folosite si pentru comprim ari obi snuite, diferent a fat a de o arhiv a ZIP obi snuit a ind doar existent a unui director denumit META-INF, ce cont ine diverse informat ii auxiliare legate de aplicat ia sau clasele arhivate. Un sier JAR poate creat folosind utilitarul jar aat n distribut ia 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 aplicat ii n forma sa nal a este redus a; minimizarea timpului de ncarcare a unui applet: dac a appletul ( siere class, resurse, etc) este compresat ntr-o arhiv a JAR, el poate nc arcat ntr-o singur a tranzact ie HTTP, f ar a a deci nevoie de a deschide c ate o conexiune nou a pentru ecare sier; securitate - arhivele JAR pot semnate electronic mecanismul pentru lucrul cu siere JAR este parte integrata a platformei Java.

6.3.1

Folosirea utilitarului jar

Arhivatorul jar se g ase ste n subdirectorul bin al directorului n care este instalat kitul J2SDK. Mai jos sunt prezentate pe scurt operat iile uzuale: Crearea unei arhive jar cf arhiva.jar fi sier(e)-intrare Vizualizare cont inutului jar tf nume-arhiva Extragerea cont inutului jar xf arhiva.jar Extragerea doar a unor siere jar xf arhiva.jar fi sier(e)-arhivate Executarea unei aplicat ii java -jar arhiva.jar Deschiderea unui applet arhivat <applet code=A.class archive="arhiva.jar" ...> Exemple: Arhivarea a dou a siere class: jar cf classes.jar A.class B.class arhivarea tuturor sierelor din directorul curent: jar cvf allfiles.jar *

6.3. ARHIVE JAR

159

6.3.2

Executarea aplicat iilor arhivate

Pentru a rula o aplicat ie mpachetat a ntr-o arhiv a JAR trebuie s a facem cunoscut a interpretorului numele clasei principale a aplicat iei. S a consider am urm atorul exemplu, n care dorim s a arhiv am clasele aplicat iei descrise mai sus, n care clasa principal a era Matematica.java. Din directorul clase vom lansa comanda: jar cvf mate.jar geometrie analiza algebra Matematica.class In urma acestei comenzi vom obtine arhiva mate.jar. Dac a vom ncerca s a lans am n execut ie aceast a arhiv a prin comanda java -jar mate.jar vom obt ine urm atoarea eroare: Failed to load Main-Class manifest from mate.jar. Aceasta nseamna c a n serul Manifest.mf ce se gase ste n directorul META-INF trebuie s a nregistr am clasa principal a a aplicat iei. Acest lucru l vom face n doi pa si: se creeaz a un sier cu un nume oarecare, de exemplu manifest.txt, n care vom scrie: Main-Class: Matematica adaug am aceast a informat ie la sierul manifest al arhivei mate.jar: jar uvfm mate.jar manifest.txt Ambele operat ii 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 a c a fac and dublu-click pe o arhiv a JAR va lansat a n execut ie aplicat ia mpachetat a n acea arhiv a (dac a exist a o clas a principal a).

160

CAPITOLUL 6. ORGANIZAREA CLASELOR

Capitolul 7 Colect ii
7.1 Introducere

O colect ie este un obiect care grupeaz a mai multe elemente ntr-o singur a unitate. Prin intermediul colect iilor vom avea acces la diferite tipuri de date cum ar vectori, liste nl ant uite, stive, mult imi matematice, tabele de dispersie, etc. Colect iile sunt folosite at at pentru memorarea si manipularea datelor, c at si pentru transmiterea unor informat ii de la o metod a la alta. Tipul de date al elementelor dintr-o colect ie este Object, ceea ce nseamn a c a mult imile reprezentate sunt eterogene, put and include obiecte de orice tip. Incep and cu versiunea 1.2, n Java colect iile sunt tratate ntr-o manier a unitar a, ind organizate ntr-o arhitectur a foarte ecient a si exibil a ce cuprinde: Interfet e: tipuri abstracte de date ce descriu colect iile si permit utilizarea lor independent de detaliile implement arilor. Implement ari: implement ari concrete ale interfet elor ce descriu colect ii. Aceste clase reprezint a tipuri de date reutilizabile. Algoritmi: metode care efectueaz a diverse operat ii utile cum ar c autarea sau sortarea, denite pentru obiecte ce implementeaz a interfet ele ce descriu colect ii. Ace sti algoritmi se numesc si polimorci deoarece pot folosit i pe implement ari diferite ale unei colect ii, reprezent and elementul de funct ionalitate reutilizabil a. Utilizarea colect iilor ofer a avantaje evidente n procesul de dezvoltare a unei aplicat ii. Cele mai importante sunt: 161

162

CAPITOLUL 7. COLECT II

Reducerea efortului de programare: prin punerea la dispozit ia programatorului a unui set de tipuri de date si algoritmi ce modeleaz a structuri si operat ii des folosite n aplicat ii. Cre sterea vitezei si calit a tii programului: implement arile efective ale colect iilor sunt de nalt a performant a si folosesc algoritmi cu timp de lucru optim. Astfel, la scrierea unei aplicat ii putem s a ne concentr am eforturile asupra problemei n sine si nu asupra modului de reprezentare si manipulare a informat iilor.

7.2

Interfet e ce descriu colect ii

Interfet ele reprezint a nucleul mecanismului de lucru cu colect ii, scopul lor ind de a permite utilizarea structurilor de date independent de modul lor de implementare.

Collection modeleaz a o colect ie la nivelul cel mai general, descriind un grup de obiecte numite si elementele sale. Unele implement ari ale acestei interfet e permit existent a elementelor duplicate, alte implement ari nu. Unele au elementele ordonate, altele nu. Platforma Java nu ofer a nici o implementare direct a a acestei interfet e, ci exist a doar implement ari ale unor subinterfet e 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. INTERFET E CE DESCRIU COLECT II

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 a not iunea de mult ime n sens matematic. O mult ime nu poate avea elemente duplicate, mai bine zis nu poate cont ine dou a obiecte o1 si o2 cu proprietatea o1.equals(o2). Mo stene ste metodele din Collection, f ar a a avea alte metode specice. Dou a dintre clasele standard care ofer a implement ari concrete ale acestei interfet e sunt HashSet si TreeSet.

SortedSet este asem an atoare cu interfat a Set, diferent a principal a const and n faptul c a elementele dintr-o astfel de colect ie sunt ordonate ascendent. Pune la dispozit ie operat ii care beneciaz a de avantajul ordon arii elementelor. Ordonarea elementelor se face conform ordinii lor naturale, sau conform cu ordinea dat a de un comparator specicat la crearea colect iei si este ment inut a automat la orice operat ie efectuat a asupra mult imii. Singura conditt ie este ca, pentru orice dou a obiecte o1, o2 ale colect iei, apelul o1.compareT o(o2) (sau comparator.compare(o1, o2), dac a este folosit un comparator) trebuie s a e valid si s a nu provoace except ii. Fiind subclas a a interfet ei Set, mo stene ste metodele acesteia, oferind metode suplimentare ce tin cont de faptul c a mult imea este sortat a: public interface SortedSet extends Set { // Subliste SortedSet subSet(Object fromElement, Object toElement); SortedSet headSet(Object toElement);

164

CAPITOLUL 7. COLECT II

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

List descrie liste (secvent e) de elemente indexate. Listele pot contine duplicate si permit un control precis asupra pozit iei unui element prin intermediul indexului acelui element. In plus, fat ade metodele denite de interfat a Collection, avem metode pentru acces pozit ional, c autare si iterare avansat a. Denit ia interfet ei este: 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. INTERFET E CE DESCRIU COLECT II Clase standard care implementeaz a aceast a interfat a sunt: ArrayList, LinkedList, Vector.

165

Map descrie structuri de date ce asociaz a ecarui element o cheie unic a, dup a care poate reg asit. Obiectele de acest tip nu pot cont ine chei duplicate si ecare cheie este asociat a la un singur element. Ierarhia interfet elor derivate din Map este independent a de ierarhia derivat a din Collection. Denitt ai interfet ei este prezentat a mai jos: 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. COLECT II

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

SortedMap este asem an atoare cu interfat a Map, la care se adaug a faptul c a mult imea cheilor dintr-o astfel de colect ie este ment inut a ordonat a ascendent conform ordinii naturale, sau conform cu ordinea dat a de un comparator specicat la crearea colect iei. Este subclasa a interfet ei Map, oferind metode suplimentare pentru: extragere de subtabele, aarea primei/ultimei chei, aarea comparatorului folosit pentru ordonare. Denit ia interfet ei este dat a mai jos: 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 a aceast a interfat a este TreeMap.

7.3

Implement ari ale colect iilor

Inainte de versiunea 1.2, exista un set de clase pentru lucrul cu colect ii, ns a acestea nu erau organizate pe ierarhia de interfet e prezentat a n sect iunea anterioar a. Aceste clase sunt n continuare disponibile si multe dintre ele au fost adaptate n a sa fel nc at s a se integreze n noua abordare. Pe l ang a acestea au fost create noi clase corespunz atoare interfet elor denite, chiar dac a funct ionalitatea lor era aproape identic a cu cea a unei clase anterioare.

ALE COLECT 7.3. IMPLEMENTARI IILOR

167

Clasele de baz a care implementeaz a interfet e ce descriu colect ii au numele de forma < Implementare >< Interf ata >, unde implementare se refer a la structura intern a folosit a pentru reprezentarea mult imii, si sunt prezentate n tabelul de mai jos, mpreun a cu interfet ele corespunz atoare (clasele din vechiul model sunt trecute pe r andul de jos): Interfat a Set SortedSet List Clasa HashSet TreeSet ArrayList, LinkedList Vector Map HashMap Hashtable SortedMap TreeMap A sadar se observ a existent a unor clase care ofer a aceea si funct ionalite, cum ar ArrayList si Vector, HashMap si Hashtable. Pe l ang a organizarea ierarhic a a interfet elor implementate, clasele ce descriu colect ii sunt de asemenea concepute ntr-o manier a ierarhic a, ca n gura de mai jos: AbstractCollection - AbstractSet, AbstractList - HashSet, TreeSet... Vector-Stack AbstractMap - HashMap, TreeMap, HashTable In vechea ierarhie: Dictionary - Hashtable - Properties Evident, implementarea interfet elor este explicit realizat a la nivelul superclaselor abstracte, acestea oferind de altfel si implement ari concrete pentru multe din metodele denite de interfet e. In general, clasele care descriu colect ii au unele tr asaturi comune, cum ar : permit elementul null, sunt serializabile, au denit a metoda clone,

168

CAPITOLUL 7. COLECT II

au denit a metoda toString, care returneaz a o reprezentare ca sir de caractere a colect iei respective, permit crearea de iteratori pentru parcurgere, au at at constructor f ar a argumente c at si un constructor care accept a ca argument o alt a colect ie except and clasele din arhitectura veche, nu sunt sincronizate (vezi Fire de execut ie).

7.4

Folosirea ecient a a colect iilor

Dup a cum am vazut, ecare interfat a ce descrie o colect ie are mai multe implement ari. De exemplu, interfat a List este implementat a de clasele ArrayList si LinkedList, prima ind n general mult mai folosit a. De ce exist a atunci si clasa LinkedList ? Raspunsul const a n faptul c a folosind reprezent ari diferite ale mult imii gestionate putem obt ine performante mai bune n funct ie de situat ie, prin realizarea unor compromisuri ntre spat iul necesar pentru memorarea datelor, rapiditatea reg asirii acestora si timpul necesar actualiz arii colect iei n cazul unor modic ari. S a consider am un exemplu ce creeaza o list a folosind ArrayList, respectiv LinkedList si execut a diverse operat ii pe ea, cronometr and timpul necesar realiz arii acestora: 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 . currentTimeMillis () ; for ( int i =0; i < N ; i ++) lst . add ( new Integer ( i ) ) ; long t2 = System . currentTimeMillis () ; System . out . println ( " Add : " + ( t2 - t1 ) ) ; }

A COLECT 7.4. FOLOSIREA EFICIENTA IILOR


public static void testGet ( List lst ) { long t1 = System . currentTimeMillis () ; for ( int i =0; i < N ; i ++) lst . get ( i ) ; long t2 = System . currentTimeMillis () ; System . out . println ( " Get : " + ( t2 - t1 ) ) ; } public static void testRemove ( List lst ) { long t1 = System . currentTimeMillis () ; for ( int i =0; i < N ; i ++) lst . remove (0) ; long t2 = System . currentTimeMillis () ; 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 performant e medii, exprimat i n secunde, sunt dat i n tabelul de mai jos: ArrayList add 0.12 get 0.01 remove 12.05 LinkedList 0.14 87.45 0.01

A sadar, ad augarea elementelor este rapid a pentru ambele tipuri de liste. ArrayList ofer a acces n timp constant la elementele sale si din acest motiv folosirea lui get este rapid a, n timp ce pentru LinkedList este extrem de lent a, deoarece ntr-o list a nlant uit a accesul la un element se face prin

170

CAPITOLUL 7. COLECT II

parcurgerea secvent ial a a listei p an a la elementul respectiv. La operat iunea de eliminare, folosirea lui ArrayList este lent a deoarece elementele r amase sufer a un proces de reindexare (shift la st anga), n timp ce pentru LinkedList este rapid a si se face prin simpla schimbare a unei leg aturi. Deci, ArrayList se comport a bine pentru cazuri n care avem nevoie de reg asirea unor elemente la pozit ii diferite n list a, iar LinkedList funct ioneaza ecient atunci c and facem multe operat ii de modicare ( stergeri, inser ari).

Concluzia nu este c a una din aceste clase este mai bun a dec at cealalt a, ci c a exist a diferent e substant iale n reprezentarea si comportamentul diferitelor implement ari si c a alegerea unei anumite clase pentru reprezentarea unei mult imi de elemente trebuie s a se fac a n funct ie de natura problemei ce trebuie rezolvat a.

7.5

Algoritmi polimorci

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

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

171

ll - populeaza o lista cu un anumit element repetat de un num ar de ori; copy - copie elementele unei liste n alta; min - returneaz a minimul dintr-o colect ie; max - returneaz a maximul dintr-o colect ie; swap - interschimb a elementele de la dou a pozit ii specicate ale unei liste; enumeration - returneaza o enumerare a elementelor dintr-o colect ie; unmodiableTipColectie - returneaz a o instant a care nu poate modicat a a colect iei respective; synchronizedTipColectie - returneaz a o instant a sincronizat a a unei colect ii (vezi Fire de execut ie).

7.6

Tipuri generice

Tipurile generice, introduse n versiunea 1.5 a limbajului Java, simplic a lucrul cu colect ii, permit and tipizarea elementelor acestora. Denirea unui tip generic se realizeaz a prin specicarea ntre paranteze unghiulare a unui tip de date Java, efectul ind impunerea tipului respectiv pentru toate elementele colect iei: <TipDate>. S a consider am un exemplu de utilizare a colect iilor nainte si dup a introducerea tipurilor generice: // 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 a poate cont ine obiecte de orice tip, de si am dori ca elementele s a e doar numere ntregi. Mai mult, trebuie s a facem cast explicit de la tipul Object la Integer atunci c and prelu am valoarea unui element. Folosind tipuri generice, putem rescrie secvent a astfel:

172

CAPITOLUL 7. COLECT II

// Dupa 1.5, folosind tipuri generice ArrayList<Integer> list = new ArrayList<Integer>(); list.add(new Integer(123)); int val = list.get(0).intValue(); Dac a utiliz am si mecanismul de autoboxing, obt inem o variant a mult simplicat a a secvent ei init iale: // 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 n cadrul unei colect ii a unui element necorespunz ator ca tip va produce o eroare la compilare, spre deosebire de varianta anterioar a ce permitea doara aruncarea unor except ie de tipul ClassCastException n cazul folosirii incorecte a tipurilor.

7.7

Iteratori si enumer ari

Enumer arile si iteratorii descriu modalit a ti pentru parcurgerea secvent ial aa unei colect ii, indiferent dac a aceasta este indexat a sau nu. Ei sunt descri si de obiecte ce implementeaz a interfet ele Enumeration, respectiv Iterator sau ListIterator. Toate clasele care implementeaz a colect ii au metode ce returneaz a o enumerare sau un iterator pentru parcurgerea elementelor lor. Deoarece funct ionalitatea interfet ei Enumeration se reg ase ste n Iterator, aceasta din urm a este preferat a n noile implement ari ale colect iilor. Metodele uzuale ale acestor interfet e sunt prezentate mai jos, mpreun a cu modalitatea lor de folosire, semnicat iile 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 S I 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 colect ia pe care o parcurg, cei de tip ListIterator permit si inserarea unui element la pozit ia curent a, respectiv modicarea elementului curent, precum si iterarea n ambele sensuri. Iteratorii sunt preferat i enumer arilor datorit a posibilit a tii lor de a act iona asupra colect iei pe care o parcurg prin metode de tip remove, add, set dar si prin faptul c a denumirile metodelor sunt mai concise.

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

174

CAPITOLUL 7. COLECT II

In exemplul de mai jos punem ntr-un vector numerele de la 1 la 10, le amestec am, dup a care le parcurgem element cu element folosind un iterator, 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 ) ; } }

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

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

175

176

CAPITOLUL 7. COLECT II

Capitolul 8 Serializarea obiectelor


8.1 Folosirea serializ arii

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

178

CAPITOLUL 8. SERIALIZAREA OBIECTELOR tip de persistent a a obiectelor se nume ste persistent a u soar a, ntruc at ea trebuie efectuat a explicit de c atre programator si nu este realizat a automat de c atre sistem.

Compensarea diferent elor ntre sisteme de operare - transmiterea unor informat ii ntre platforme de lucru diferite se realizeaz a unitar, independent de formatul de reprezentare a datelor, ordinea octet ilor sau alte detalii specice sistemelor repective. Transmiterea datelor n ret ea - Aplicat iile ce ruleaz a n ret ea pot comunica ntre ele folosind uxuri pe care sunt trimise, respectiv recept ionate obiecte serializate. RMI (Remote Method Invocation) - este o modalitate prin care metodele unor obiecte de pe o alt a ma sin a pot apelate ca si cum acestea ar exista local pe ma sina pe care ruleaz a aplicat ia. Atunci c and este trimis un mesaj c atre un obiect remote (de pe alt a ma sin a), serializarea este utilizat a pentru transportul argumentelor prin ret ea si pentru returnarea valorilor. Java Beans - sunt componente reutilizabile, de sine st at atoare ce pot utilizate n medii vizuale de dezvoltare a aplicat iilor. Orice component a Bean are o stare denit a de valorile implicite ale propriet a tilor sale, stare care este specicat a n etapa de design a aplicat iei. Mediile vizuale folosesc mecanismul serializ arii pentru asigurarea persistent ei componentelor Bean. Un aspect important al serializ arii este c a nu salveaz a doar imaginea unui obiect ci si toate referint ele la alte obiecte pe care acesta le cont ine. Acesta este un proces recusiv de salvare a datelor, ntruc at celelalte obiectele referite de obiectul care se serializeaz a pot referi la r andul lor alte obiecte, si a sa mai departe. A sadar referint ele care construiesc starea unui obiect formeaz a o ntreag a ret ea, ceea ce nseamn a c a un algoritm general de salvare a st arii unui obiect nu este tocmai facil. In cazul n care starea unui obiect este format a doar din valori ale unor variabile de tip primitiv, atunci salvarea informat iilor ncapsulate n acel obiect se poate face si prin salvarea pe r and a datelor, folosind clasa DataOutputStream, pentru ca apoi s a e restaurate prin metode ale clasei DataInputStream, dar, a sa cum am vazut, o asemenea abordare nu este

8.1. FOLOSIREA SERIALIZARII

179

n general sucient a, deoarece pot ap area probleme cum ar : variabilele membre ale obiectului pot instant e ale altor obiecte, unele c ampuri pot face referint a la acela si obiect, etc.

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

8.1.1

Serializarea tipurilor primitive

Serializarea tipurilor primitive poate realizat a e prin intermediul uxurilor DataOutputStream si DataInputStream, e cu ObjectOutputStream si ObjectInputStream. Acestea implementeaz a interfet ele DataInput, respectiv DataOutput ce declar a metode de tipul readTipPrimitiv, respectiv writeTipPrimitiv pentru scrierea/citirea datelor primitive si a sirurilor de 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 informat iilor scrise n exemplul de mai sus se va face astfel: 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 a prin intermediul uxurilor denite de clasele ObjectOutputStream (pentru salvare) si ObjectInputStream (pentru restaurare). Acestea sunt uxuri de procesare, ceea ce nseamna c a vor folosite mpreuna cu alte uxuri pentru scrierea/citirea efectiv aa 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 si valorile tuturor c ampurile serializabile ale obiectului. Referint ele la alte obiecte serializabile din cadrul obiectului curent vor duce automat la serializarea acestora iar referint ele multiple c atre un acela si obiect sunt codicate utiliz and un algoritm care s a poat a reface ret eaua de obiecte la aceea si stare ca atunci c and obiectul original a fost salvat. Clasele ObjectInputStream si ObjectOutputStream implementeaz a interfet ele ObjectInput, respectiv ObjectOutput care extind DataInput, respectiv DataOutpu ceea ce nseamn a c a, pe l ang a metodele dedicate serializ arii obiectelor, vor exista si metode pentru scrierea/citirea datelor primitive si a sirurilor de caractere. Metodele pentru serializarea obiectelor sunt: writeObject, pentru scriere si readObject, pentru restaurare.

8.1.3

Clasa ObjectOutputStream

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

8.1. FOLOSIREA SERIALIZARII

181

Exemplul de mai jos construie ste un obiect de tip Date si l salveaz a n sierul test.ser, mpreun a cu un obiect de tip String. Evident, sierul rezultat va cont ine informat iile reprezentate n format binar. 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 a interfat a DataOutput, pe l anga metoda de scriere a obiectelor, clasa pune la dispozitie si metode de tipul writeTipPrimitiv pentru serializarea tipurilor de date primitive si a sirurilor de caractere, astfel nc at apeluri ca cele de mai jos sunt permise : out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); Metoda writeObject arunc a except ii de tipul IOException si derivate din aceasta, mai precis NotSerializableException dac a obiectul primit ca argument nu este serializabil, sau InvalidClassException dac a sunt probleme cu o clas a necesar a n procesul de serializare. Vom vedea n continuare c a un obiect este serializabil dac a este instant a a unei clase ce implementeaz a interfat a Serializable.

8.1.4

Clasa ObjectInputStream

Odat a ce au fost scrise obiecte si tipuri primitive de date pe un ux, citirea acestora si reconstruirea obiectelor salvate se va face printr-un ux de intrare de tip ObjectInputStream. Acesta este de asemenea un ux de procesare si va trebui asociat cu un ux pentru citirea efectiv a a datelor, cum ar FileInputStream pentru date salvate ntr-un sier. Secvent a uzual a pentru 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 informat iilor scrise n exemplul de mai sus se va face astfel: 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 a metoda readObject are tipul returnat Object, ceea ce nseamn a c a trebuie realizat a explicit conversia la tipul corespunzator obiectului citit: Date date = in.readObject(); // gresit Date date = (Date)in.readObject(); // corect

Atent ie Ca si la celelalte uxuri de date care implemeteaz a interfat a DataInput citirea dintr-un ux de obiecte trebuie s a se fac a exact n ordinea n carea acestea au fost scrise, altfel vor ap area evident except ii n procesul de deserializare.

Clasa ObjectInputStream implementeaz a interfat a DataInput deci, pe l ang a metoda de citire a obiectelor, clasa pune la dispozit ie si metode de tipul readTipPrimitiv pentru citirea tipurilor de date primitive si a sirurilor 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 a si numai dac a clasa din care face parte implementeaz a interfat a Serializable. A sadar, dac a dorim ca instant ele unei clase s a poat a serializate, clasa respectiv a trebuie s a implementeze, direct sau indirect, interfat a Serializable.

8.2.1

Implementarea interfet ei Serializable

Interfat a Serializable nu cont ine nici o declarat ie de metod a sau constant a, singurul ei scop ind de a identica clasele ale c aror obiecte sunt serializabile. Denit ia sa complet a este: package java.io; public interface Serializable { // Nimic ! } Declararea claselor ale c aror instant e trebuie s a e serializate este a sadar extrem de simpl a, ind f acut a prin simpla implementare a interfet ei Serializable: public class ClasaSerializabila implements Serializable { // Corpul clasei } Orice subclas a a unei clase serializabile este la r andul ei serializabil a, ntruc at implementeaz a indirect interfat a Serializable. In situat ia n care dorim s a declar am o clas a serializabil a dar superclasa sa nu este serializabil a, atunci trebuie s a avem n vedere urm atoarele lucruri: 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 n sect iunea referitoare la personalizarea serializ arii. Superclasa trebuie s a aib a obligatoriu un constructor accesibil f ar a argumente, acesta ind utilizat pentru init ializarea variabilelor mo stenite n procesul de restaurare al unui obiect. Variabilele proprii vor init ializate cu valorile de pe uxul de intrare. In lipsa unui constructor accesibil f ar a argumente pentru superclas a, va generat a o except ie la execut ie.

184

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

In procesul serializ arii, dac a este nt alnit un obiect care nu implementeaz a interfat a Serializable atunci va generat a o except ie de tipul NotSerializableEx ce va identica respectiva clas a neserializabil a.

8.2.2

Controlul serializ arii

Exist a cazuri c and dorim ca unele variabile membre ale unui obiect s a nu e salvate automat n procesul de serializare. Acestea sunt cazuri comune atunci c and respectivele c ampuri reprezint a informat ii condent iale, cum ar parole, sau variabile temporare pe care nu are rost s a le salv am. Chiar declarate private n cadrul clasei aceste c ampuri particip a la serializare. Pentru ca un c amp s a nu e salvat n procesul de serializare el trebuie declarat cu modicatorul transient si trebuie s a e ne-static. De exemplu, declararea unei variabile membre temporare ar trebui facut a astfel: transient private double temp; // Ignorata la serializare Modicatorul static anuleaz a efectul modicatorului transient. Cu alte cuvinte, variabilele de clas a particip a obligatoriu la serializare. static transient int N; // Participa la serializare In exemplele urm atoare c ampurile marcate DA particip a la serializare, cele marcate NU, nu particip a iar cele marcate cu Exceptie vor provoca except ii de tipul NotSerializableException. Listing 8.1: Modicatorii static si transient
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 a un obiect ce trebuie serializat are referint e la obiecte neserializabile, atunci va generat a o except ie de tipul NotSerializableException. 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 c and o clas a serializabila deriva dintr-o alt a clas a, salvarea c ampurilor clasei p arinte se va face doar dac a si aceasta este serializabil a. In caz contrar, subclasa trebuie s a salveze explicit si c ampurile mo stenite. Listing 8.3: Serializarea c ampurilor mo stenite
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 aplicat ie care efectueaz a salvarea si restaurarea unor obiecte din cele trei clase prezentate mai sus. Listing 8.4: Testarea serializ arii
import java . io .*; public class Exemplu { public static void test ( Object obj ) throws IOException { // Salvam FileOutputStream fos = new FileOutputStream ( " fisier . ser " ) ; ObjectOutputStream out = new ObjectOutputStream ( 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 ObjectInputStream ( fis ) ; try { obj = in . readObject () ; } catch ( Cl as sNo tF oun dE xc ept io 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 li z a bl e E x ce p t io 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 serializ arii obiectelor

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

188

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

anumitor cerint e. In cazul n care nu dorim s a nlocuim complet mecanismul standard, putem s a folosim metodele defaultWriteObject, respectiv defaultReadObject care descriu procedurile implicite de serializare. Forma general a de implementare a metodelor writeObject si readObject 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 si readObject sunt responsabile cu serializarea clasei n care sunt denite, serializarea superclasei sale ind facut a automat ( si implicit). Dac a ns a o clas a trebuie sa- si coordoneze serializarea proprie cu serializarea superclasei sale, atunci trebuie s a implementeze interfat a Externalizable.

8.3.1

Controlul versiunilor claselor

S a presupunem c a dorim s a realiz am o aplicat ie care s a tin a evident a angajat ilor unei companii. Evident, vom avean nevoie de o clas a care s a reprezinte

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

189

not iunea de angjat. O variant a simplicat a a acesteia ar putea ar ata astfel: Listing 8.5: Prima variant a a clasei Angajat
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 a o mic a aplicat ie care permite introducerea de angajat i si salvarea lor ntr-un sier. La ecare pornire a aplicat iei, vor citite datele din sier astfel nc at programul va actualiza n permanent a lista angajat ilor cu noi persoane. Listing 8.6: Aplicat ia de gestionare a angajat ilor
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 ObjectInputStream ( fis ) ;

190

CAPITOLUL 8. SERIALIZAREA OBIECTELOR


ang = ( ArrayList ) in . readObject () ; } catch ( File NotFo undEx cepti on 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 " ) ; ObjectOutputStream out = new ObjectOutputStream ( fos ) ; out . writeObject ( ang ) ; } public void adaugare () throws IOException { BufferedReader stdin = new BufferedReader ( new InputStreamReader ( 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 urm atoarea. Dup a introducerea unui num ar sucient de mare de angajat i n sier, clasa Angajat este modicat a prin ad augarea unei noi variabil a membre care s a ret ina adresa. La execut ia aplicat iei noastre, procedura de citire a angajat ilor din sier nu va mai funct iona, produc and o except ie de tipul InvalidClassException. Aceast aproblem a ar ap arut chiar dac a variabila ad augat a era declarat a de tip transient. De ce se nt ampl a acest lucru ? Explicat ia const a n faptul c a mecanismul de serializare Java este foarte atent cu signatura claselor serializate. Pentru ecare obiect serializat este calculat automat un num ar reprezentat pe 64 de bit i, care reprezint a un fel de amprent a a clasei obiectului. Acest num ar, denumit serialVersionUID, este generat pornind de la diverse informat ii ale clasei, cum ar variabilele sale membre, (dar nu numai) si este salvat n procesul de serializare mpreun a cu celelalte date. In plus, orice modicare semnicativ a a clasei, cum ar ad augarea unui nou c amp, va determina modicarea num arului s au de versiune. La restaurarea unui obiect, num arul de versiune salvat n forma serializat a va reg asit si comparat cu noua semn atur a a clasei obiectului. In cazul n care acestea nu sunt egale, va generat a o except ie de tipul InvalidClassException si deserializarea nu va f acut a. Aceast a abordare extrem de precaut a este foarte util a pentru prevenirea unor anomalii ce pot ap area c and dou a versiuni de clase sunt incompatibile, dat poate sup ar atoare atunci c and modic arile aduse clasei nu stric a compatibilitatea cu vechea versiune. In aceast a situat ie trebuie s a comunic am explicit c a cele dou a clase sunt compatibile. Acest lucru se realizeaz a prin setarea manual a a variabilei serialVersionUID n cadrul clasei dorite, ad aug and pur si simplu c ampul:

192

CAPITOLUL 8. SERIALIZAREA OBIECTELOR

static final long serialVersionUID = /* numar_serial_clasa */; Prezent a variabilei serialVersionUID printre membrii unei clase va informa algoritmul de serialzare c a nu mai calculeze num arul de serie al clasei, ci s a-l foloseasc a pe cel specicat de noi. Cum putem aa ns a num arul de serie al vechii clase Angajat care a fost folosit a anterior la salvarea angajat ilor ? Utilitarul serialVer permite generarea num arului serialVersionUID pentru o clas a specicat a. A sadar, trebuie s a recompil am vechea clas a Angajat si s a-i a am num arul de serie astfel: serialVer Angajat. Rezultatul va : Angajat: static final long serialVersionUID = 5653493248680665297L; Vom rescrie noua clas a Angajat astfel nc at s a e compatibil a cu cea veche astfel: Listing 8.7: Variant a compatibil a a clasei Angajat
import java . io .*; class Angajat implements Serializable { static final long serialVersionUID = 5653493248680665297 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 + " ) " ; } }

Aplicat ia noastr a va funct iona acum, ns a rubrica adres a nu va init ializat a n nici un fel (va null), deoarece ea nu exista n formatul original. La noua

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

193

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

8.3.2

Securizarea datelor

Dup a cum am v azut membrii privat i, cum ar parola din exemplul de mai sus, particip a la serializare. Problema const a n faptul c a, de si n format binar, informat iile unui obiect serializat nu sunt criptate n nici un fel si pot reg asite cu u surint a, ceea ce poate reprezenta un inconvenient atunci c and exist a c ampuri condent iale. Rezolvarea acestei probleme se face prin modicarea mecanismului implicit de serializare, implement and metodele readObject si writeObject, precum si prin utilizarea unei funct ii de criptare a datelor. Varianta securizat a a clasei Angajat din exemplul anterior ar putea ar ata astfel: Listing 8.8: Varianta securizat a a clasei Angajat
import java . io .*; class Angajat implements Serializable { static final long serialVersionUID = 5653493248680665297 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 ( ObjectOutputStream stream ) throws IOException { parola = criptare ( parola , 3) ; stream . defaultWriteObject () ; parola = criptare ( parola , -3) ; } private void readObject ( ObjectInputStream stream ) throws IOException , C la ssN ot Fou nd Exc ep ti on { stream . defaultReadObject () ; parola = criptare ( parola , -3) ; } }

8.3.3

Implementarea interfet ei Externalizable

Pentru un control complet, explicit, al procesului de serializare, o clas a trebuie s a implementeze interfat a Externalizable. Pentru instant e ale acestor clase doar numele clasei este salvat automat pe uxul de obiecte, clasa ind responsabil a cu scrierea si citirea membrilor s ai si trebuie s a se coordoneze cu superclasele ei. Denit ia interfet ei 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; } A sadar, aceste clase trebuie s a implementeze obligatoriu metodele writeExternal si readExternal n care se va face serializarea complet a a obiectelor si coordonarea cu superclasa ei. Uzual, interfat a Externalizable este folosit a n situat ii n care se dore ste mbun at a tirea performant elor algoritmului standard, mai exact cre sterea vitezei procesului de serializare.

8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR

195

Mai jos este prezentat a o clas a simpl a si modalitatea de rescriere a sa folosind interfat a 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 stie c a nu putem copia valoarea unui obiect prin instruct iunea de atribuire. O secvent a de forma: TipReferinta o1 = new TipReferinta(); TipReferinta o2 = o1; nu face dec at s a declare o nou a variabil a o2 ca referint a la obiectul referit de o1 si nu creeaz a sub nici o form a un nou obiect. O posibilitate de a face o copie a unui obiect este folosirea metodei clone denit a n clasa Object. Aceasta creeaz a un nou obiect si init ializeaz a toate variabilele sale membre cu valorile obiectului clonat. TipReferinta o1 = new TipReferinta(); TipReferinta o2 = (TipReferinta) o1.clone(); Decient a acestei metode este c a nu realizeaz a duplicarea ntregii ret ele de obiecte corespunz atoare obiectului clonat. In cazul n care exist a c ampuri referint a la alte obiecte, obiectele referite nu vor mai clonate la r andul lor. O metod a clone care s a realizeze o copie efectiv a a unui obiect, mpreuna cu copierea tuturor obiectelor referite de c ampurile acelui obiect poate implementat a prin mecanismul serializ arii astfel: 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 Interfat a grac a cu utilizatorul


9.1 Introducere

Interfat a grac a cu utilizatorul (GUI), este un termen cu nt eles larg care se refer a la toate tipurile de comunicare vizual a ntre un program si utilizatorii s ai. Aceasta este o particularizare a interfet ei cu utilizatorul (UI), prin care vom ntelege conceptul generic de interact iune dintre program si utilizatori. Limbajul Java pune la dispozit ie numeroase clase pentru implementarea diverselor funct ionalitati UI, ns a ne vom ocupa n continuare de cele care permit realizarea intefet ei grace cu utilizatorul (GUI). De la aparit ia limbajului Java, bibliotecile de clase care ofer a servicii grace au suferit probabil cele mai mari schimb ari n trecerea de la o versiune la alta. Acest lucru se datoreaz a, pe de o parte dicult a tii legate de implementarea not iunii de portabilitate, pe de alt a parte nevoii de a integra mecanismele GUI cu tehnologii ap arute si dezvoltate ulterior, cum ar Java Beans. In momentul actual, exist a dou a modalit a ti de a crea o aplicat ie cu interfat a grac a si anume: AWT (Abstract Windowing Toolkit) - este API-ul init ial pus la dispozit ie ncep and cu primele versiuni de Java; Swing - parte dintr-un proiect mai amplu numit JFC (Java Foundation Classes) creat n urma colabor arii dintre Sun, Netscape si IBM, Swing se bazeaz a pe modelul AWT, extinz and funct ionalitatea acestuia si ad aug and sau nlocuind componente pentru dezvoltarea aplicat iilor GUI. 199

200

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

A sadar, este de preferat ca aplicat iile Java s a e create folosind tehnologia Swing, aceasta pun and la dispozit ie o palet a mult mai larg a de facilit a ti, ns a nu vom renunt a complet la AWT deoarece aici exist a clase esent iale, reutilizate n Swing. In acest capitol vom prezenta clasele de baz a si mecanismul de tratare a evenimentelor din AWT, deoarece va simplicat procesul de nt elegere a dezvolt arii unei aplicat ii GUI, dup a care vom face trecerea la Swing. In principiu, crearea unei aplicat ii grace presupune urm atoarele lucruri: Design Crearea unei suprafet e de a sare (cum ar o fereastr a) pe care vor a sezate obiectele grace (componente) care servesc la comunicarea cu utilizatorul (butoane, controale pentru editarea textelor, liste, etc); Crearea si a sezarea componentelor pe suprafat a de a sare la pozit iile corespunz atoare; Funct ionalitate Denirea unor act iuni care trebuie s a se execute n momentul c and utilizatorul interact ioneaz a cu obiectele grace ale aplicat iei; Ascultarea evenimentelor generate de obiecte n momentul interact iunii cu utilizatorul si executarea act iunilor corespunz atoare, a sa cum au fost ele denite.

9.2

Modelul AWT

Pachetul care ofer a componente AWT este java.awt. Obiectele grace sunt derivate din Component, cu except ia meniurilor care descind din clasa MenuComponent. A sadar, prin not iunea de component a vom ntelege n continuare orice obiect care poate avea o reprezentare grac a si care poate interactiona cu utilizatorul. Exemple de componente sunt ferestrele, butoanele, listele, bare de delare, etc. Toate componentele AWT sunt dente de clase proprii ce se gasesc n pachetul java.awt, clasa Component ind superclasa abstract a a tuturor acestor clase. Crearea obiectelor grace nu realizeaz a automat si a sarea lor pe ecran. Mai nt ai ele trebuie a sezate pe o suprafata de a sare, care poate o fereastr a

9.2. MODELUL AWT

201

sau un applet, si vor deveni vizibile n momentul n care suprafat a pe care sunt a sate va vizibil a. O astfel de suprafat a pe care sunt plasate componente se mai nume ste container si reprezint a o instant a a unei clase derivate din Container. Clasa Container este o subclas a aparte a lui Component, ind la r andul ei superclasa tuturor suprafetelor de a sare Java. A sa cum am v azut, interfat a grac a serve ste interact iunii cu utilizatorul. De cele mai multe ori programul trebuie s a fac a o anumit a prelucrare n momentul n care utilizatorul a efectuat o act iune si, prin urmare, componentele trebuie s a genereze evenimente n funct ie de act iunea pe care au suferit-o (act iune transmis a de la tastatur a, mouse, etc.). Incep and cu versiunea 1.1 a limbajului Java, evenimentele sunt instant e ale claselor derivate din AWTEvent. A sadar, un eveniment este produs de o act iune a utilizatorului asupra unui obiect grac, deci evenimentele nu trebuie generate de programator. In schimb, ntr-un program trebuie specicat codul care se execut a la aparit ia unui eveniment. Tratarea evenimentelor se realizeaz a prin intermediul unor clase de tip listener (ascult ator, consumator de evenimente), clase care sunt denite n pachetul java.awt.event. In Java, orice component a poate consuma evenimentele generate de o alt a component a (vezi Tratarea evenimentelor). S a consider am un mic exemplu, n care cre am o fereastr a ce cont ine dou a butoane. Listing 9.1: O fereastr a cu dou a butoane
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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA


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

} }

Dup a cum vet i observa la execut ia acestui program, at at butoanele ad augate de noi c at si butonul de nchidere a ferestrei sunt funct ionale, adic a pot apasate, dar nu realizeaz a nimic. Acest lucru se nt ampl a deoarece nu am specicat nic aieri codul care trebuie s a se execute la ap asarea acestor butoane. De asemenea, mai trebuie remarcat c a nu am specicat nic aieri dimensiunile ferestrei sau ale butoanelor si nici pozitiile n acestea s a e plasate. Cu toate acestea ele sunt plasate unul l anga celalalt, f ar a s a se suprapun a iar suprafat a fereastrei este sucient de mare c at s a cuprind a ambele obiecte. Aceste fenomene sunt provocate de un obiect special de tip FlowLayout pe care l-am specicat si care se ocup a cu gestionarea ferestrei si cu plasarea componentelor ntr-o anumit a ordine pe suprafat a ei. A sadar, modul de aranjare nu este o caracteristic a a suprafet ei de a sare ci, ecare container are asociat un obiect care se ocup a cu dimensionarea si dispunerea componentelor pe suprafat a de a sare si care se numeste gestionar de pozit ionare (layout manager) (vezi Gestionarea pozit ion arii).

9.2.1

Componentele AWT

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

9.2. MODELUL AWT

203

Container - superclasa tuturor suprafet elor de a sare (vezi Suprafet e de a sare); Label - etichete simple ce pot cont ine o singur a linie de text needitabil; List - liste cu select ie simpl a sau multipl a; Scrollbar - bare de delare orizontale sau verticale; TextComponent - superclasa componentelor pentru editarea textului: TextField (pe o singur a linie) si TextArea (pe mai multe linii). Mai multe informat ii legate de aceste clase vor prezentate n sect iunea Folosirea componentelor AWT. Din cauza unor diferent e esentiale n implementarea meniurilor pe diferite 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, mo stenite din clasa Component. Acestea servesc uzual pentru aarea sau setarea atributelor obiectelor, cum ar : dimensiune, pozit ie, culoare, font, etc. si au formatul general getProprietate, respectiv setProprietate. Cele mai folosite, grupate pe tipul propriet a tii gestionate sunt: Pozit ie getLocation, getX, getY, getLocationOnScreen setLocation, setX, setY Dimensiuni getSize, getHeight, getWidth setSize, setHeight, setWidth Dimensiuni si pozit ie getBounds setBounds Culoare (text si fundal) getForeground, getBackground setForeground, setBackground

204

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

Font getFont setFont Vizibilitate setVisible isVisible Interactivitate setEnabled isEnabled

9.2.2

Suprafet e de a sare (Clasa Container)

Crearea obiectelor grace nu realizeaz a automat si a sarea lor pe ecran. Mai nt ai ele trebuie a sezate pe o suprafat a, care poate o fereastr a sau suprafat a unui applet, si vor deveni vizibile n momentul n care suprafat a respectiv a va vizibil a. O astfel de suprafat a pe care sunt plasate componentele se nume ste suprafat a de a sare sau container si reprezint a o instant a a unei clase derivat a din Container. O parte din clasele a c aror p arinte este Container este prezentat a mai jos: Window - este superclasa tututor ferestrelor. Din aceast a clas a sunt derivate: Frame - ferestre standard; Dialog - ferestre de dialog modale sau nemodale; Panel - o suprafat a f ar a reprezentare grac a folosit a pentru gruparea altor componente. Din aceast a clas a deriv a Applet, folosit a pentru crearea appleturilor. ScrollPane - container folosit pentru implementarea automat a a derul arii pe orizontal a sau vertical a a unei componente. A sadar, un container este folosit pentru a ad auga componente pe suprafat a lui. Componentele ad augate sunt memorate ntr-o list a iar pozit iile lor din aceast a list a vor deni ordinea de traversare front-to-back a acestora n cadrul containerului. Dac a nu este specicat nici un index la ad augarea unei componente, atunci ea va adaugat a pe ultima pozit ie a listei.

9.2. MODELUL AWT

205

Clasa Container cont ine metodele comune tututor suprafet elor de a sare. Dintre cele mai folosite, amintim: add - permite ad augarea unei componente pe suprafat a de a sare. O component a nu poate apart ine dec at unui singur container, ceea ce nseamn a c a pentru a muta un obiect dintr-un container n altul trebuie sa-l eliminam mai nt ai de pe containerul initial. remove - elimin a o component a de pe container; setLayout - stabile ste gestionarul de pozit ionare al containerului (vezi Gestionarea pozit ion arii); getInsets - determin a distant a rezervat a pentru marginile suprafet ei de a sare; validate - fort eaz a containerul s a rea seze toate componentele sale. Aceast a metod a trebuie apelat a explicit atunci c and ad aug am sau elimin am componente pe suprafat a de a sare dup a ce aceasta a devenit vizibil 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

9.3

Gestionarea pozit ion arii

S a consider am mai nt ai un exemplu de program Java care a seaz a 5 butoane pe o fereastr a: Listing 9.2: Pozit ionarea 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 a sata de acest program va ar ata astfel:

S a modic am acum linia marcata cu * ca mai jos, l as and neschimbat restul programului: Frame f = new Frame("Flow Layout"); f.setLayout(new FlowLayout()); Fereastra a sat a dup a aceast a modicare va avea o cu totul altfel de dispunere a componentelor sale:

9.3. GESTIONAREA POZIT IONARII

207

Motivul pentru care cele dou a ferestre arat a at at de diferit este c a folosesc gestionari de pozit ionare diferit i: GridLayout, respectiv FlowLayout. Denit ie Un gestionar de pozit ionare (layout manager) este un obiect care controleaz a dimensiunea si aranjarea (pozit ia) componentelor unui container. A sadar, modul de aranjare a componentelor pe o suprafat a de a sare nu este o caracteristic a a containerului. Fiecare obiect de tip Container (Applet, Frame, Panel, etc.) are asociat un obiect care se ocup a cu dispunerea componentelor pe suprafat a sa si anume gestionarul s au de pozit ionare. Toate clasele care instant iaza obiecte pentru gestionarea pozit ion arii implementeaz a interfat a LayoutManager. La instant ierea unui container se creeaz a implicit un gestionar de pozit ionare asociat acestuia. De exemplu, pentru o fereastr a gestionarul implict este de tip BorderLayout, n timp ce pentru un panel este de tip FlowLayout.

9.3.1

Folosirea gestionarilor de pozit ionare

A sa cum am v azut, orice container are un gestionar implicit de pozit ionare un obiect care implemeneaz a interfat a LayoutManager, acesta indu-i ata sat automat la crearea sa. In cazul n care acesta nu corespunde necesit a tilor noastre, el poate schimbat cu u surint a. Cei mai utilizat i gestionari din pachetul java.awt sunt: FlowLayout BorderLayout GridLayout CardLayout GridBagLayout

208

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

Pe l ang a ace stia, mai exist a si cei din modelul Swing care vor prezentat i n capitolul dedicat dezvolt arii de aplicat ii GUI folosind Swing. Ata sarea explicit a a unui gestionar de pozit ionare la un container se face cu metoda setLayout a clasei Container. Metoda poate primi ca parametru orice instant a a unei clase care implementeaz a interfat a LayoutManager. Secvent a de ata sare a unui gestionar pentru un container, particularizat a pentru FlowLayout, este: FlowLayout gestionar = new FlowLayout(); container.setLayout(gestionar); // sau, mai uzual: container.setLayout(new FlowLayout()); Programele nu apeleaz a n general metode ale gestionarilor de pozit ionare, dar n cazul c and avem nevoie de obiectul gestionar l putem obt ine cu metoda getLayout din clasa Container. Una din facilit a tile cele mai utile oferite de gestionarii de pozit ionare este rearanjarea componentele unui container atunci c and acesta este redimesionat. Pozit iile si dimensiunile componentelor nu sunt xe, ele ind ajustate automat de c atre gestionar la ecare redimensionare astfel nc at s a ocupe c at mai estetic suprafat a de a sare. Cum sunt determinate ns a dimensiunile implicite ale componentelor ? Fiecare clas a derivat a din Component poate implementa metodele getPreferredSize, getMinimumSize si getMaximumSize care s a returneze dimensiunea implicit a a componentei respective si limitele n afara c arora componenta nu mai poate desenat a. Gestionarii de pozit ionare vor apela aceste metode pentru a calcula dimensiunea la care vor a sa o component a. Sunt ns a situat ii c and dorim s a plas am componentele la anumite pozit ii xe iar acestea s a ram ana acolo chiar dac a redimension am containerul. Folosind un gestionar aceast a pozit ionare absolut a a componentelor nu este posibil a si deci trebuie cumva s a renunt am la gestionarea automat a a containerul. Acest lucru se realizeaz a prin trimitera argumentului null metodei setLayout: // pozitionare absoluta a componentelor in container container.setLayout(null); Folosind pozit ionarea absolut a, nu va mai ns a sucient s a ad augam cu metoda add componentele n container, ci va trebui s a specic am pozit ia si

9.3. GESTIONAREA POZIT IONARII

209

dimensiunea lor - acest lucru era f acut automat de gestionarul de pozit ionare. container.setLayout(null); Button b = new Button("Buton"); b.setSize(10, 10); b.setLocation (0, 0); container.add(b); In general, se recomand a folosirea gestionarilor de pozit ionare n toate situat iile c and acest lucru este posibil, deoarece permit programului s a aib a aceea si nfatisare indiferent de platforma si rezolut ia pe care este rulat. Pozit ionarea absolut a poate ridica diverse probleme n acest sens.

S a analizam n continuare pe ecare din gestionarii amintit i anterior.

9.3.2

Gestionarul FlowLayout

Acest gestionar a seaz a componentele pe suprafat a de a sare n ux liniar, mai precis, componentele sunt ad augate una dup a alta pe linii, n limita spat iului disponibil. In momentul c and o component a nu mai ncape pe linia curent a se trece la urm atoarea linie, de sus n jos. Ad augarea componentelor se face de la st anga la dreapta pe linie, iar alinierea obiectelor n cadrul unei linii poate de trei feluri: la st anga, la dreapta si pe centru. Implicit, componentele sunt centrate pe ecare linie iar distant a implicit a ntre componente este de 5 pixeli pe vertical a si 5 pe orizontal a. Este gestionarul implicit al containerelor derivate din clasa Panel deci si 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA


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 a sate astfel:

Redimension and fereastra astfel nc at cele cinci butoane s a nu mai ncap a pe o linie, ultimele dintre ele vor trecute pe linia urm atoare:

9.3.3

Gestionarul BorderLayout

Gestionarul BorderLayout mparte suprafat a de a sare n cinci regiuni, corespunz atoare celor patru puncte cardinale si centrului. O component a poate plasat a n oricare din aceste regiuni, dimeniunea componentei ind calculata astfel nc at s a ocupe ntreg spat iul de a sare oferit de regiunea respectiv a. Pentru a ad auga mai multe obiecte grace ntr-una din cele cinci zone, ele trebuie grupate n prealabil ntr-un panel, care va amplasat apoi n regiunea dorit a (vezi Gruparea componentelor - clasa Panel). A sadar, la ad augarea unei componente pe o suprafat a gestionat a de BorderLayou metoda add va mai primi pe l anga referint a componentei si zona n care aceasta va amplasat a, care va specicat a prin una din constantele clasei: 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 POZIT IONARII 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 a sate astfel:

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

9.3.4

Gestionarul GridLayout

Gestionarul GridLayout organizeaz a containerul ca un tabel cu r anduri si coloane, componentele ind plasate n celulele tabelului de la st anga la dreapta, ncep and cu primul r and. Celulele tabelului au dimensiuni egale iar o component a poate ocupa doar o singur a celul a. Num arul de linii si coloane vor specicate n constructorul gestionarului, dar pot modicate

212

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

si ulterior prin metodele setRows, respectiv setCols. Dac a num arul de linii sau coloane este 0 (dar nu ambele n acela si timp), atunci componentele vor plasate ntr-o singur a coloan a sau linie. De asemenea, distant a ntre componente pe orizontal a si distanta ntre r andurile tabelului pot specicate n constructor sau stabilite ulterior. 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 sase butoane ale ferestrei vor plasate pe trei r anduri si dou a coloane, astfel:

Redimensionarea ferestrei va determina redimensionarea tuturor celulelor si deci a tuturor componentelor, at at pe orizontal a c at si pe vertical a.

9.3.5

Gestionarul CardLayout

Gestionarul CardLayout trateaz a componentele ad augate pe suprafat a sa ntr-o manier a similar a cu cea a dispunerii c art ilor de joc ntr-un pachet.

9.3. GESTIONAREA POZIT IONARII

213

Suprafat a de a sare poate asem anat a cu pachetul de c art i iar ecare component a este o carte din pachet. La un moment dat, numai o singur a component a este vizibil a (cea de deasupra). Clasa dispune de metode prin care s a poat a a sat a o anumit a component a din pachet, sau s a se poat a parcurge secvent ial pachetul, ordinea componentelor ind intern a gestionarului. Principala utilitate a acestui gestionar este utilizarea mai ecient a a spat iului disponibil n situat ii n care componentele pot grupate n a sa fel nc at utilizatorul s a interact ioneze la un moment dat doar cu un anumit grup (o carte din pachet), celelalte ind ascunse. O clas a Swing care implementeaz a un mecansim similar este JTabbedPane. 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA


card1 . addActionListener ( this ) ; card2 . addActionListener ( 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 si exibil gestionar de pozit ionare din Java. La fel ca n cazul gestionarului GridLayout, suprafat a de a sare este considerat a ca ind un tabel ns a, spre deosebire de acesta, num arul de linii si de coloane sunt determinate automat, n funct ie de componentele amplasate pe suprafat a de a sare. De asemenea, n funct ie de componentele gestionate, dimensiunile celulelor pot diferite cu singurele restrict ii ca pe aceea si linie s a aib a aceea si nalt ime, iar pe coloan a aib a aceea si l a time. Spre deosebire de GridLayout, o component a poate ocupa mai multe celule adiacente, chiar de dimensiuni diferite, zona ocupat a ind referit a prin regiunea de a sare a componentei respective. Pentru a specica modul de a sare a unei componente, acesteia i este asociat un obiect de tip GridBagConstraints, n care se specic a diferite propriet a ti ale componentei referitoare la regiunea s a de a sare si la modul n care va plasat a n aceast a regiune. Leg atura dintre o component a si un obiect GridBagConstraints se realizeaz a prin metoda setConstraints: GridBagLayout gridBag = new GridBagLayout();

9.3. GESTIONAREA POZIT IONARII

215

container.setLayout(gridBag); GridBagConstraints c = new GridBagConstraints(); //Specificam restrictiile referitoare la afisarea componentei . . . gridBag.setConstraints(componenta, c); container.add(componenta); A sadar, nainte de a ad auga o component a pe suprafat a unui container care are un gestionar de tip GridBagLayout, va trebui s a specic am anumit i parametri (constr angeri) referitori la cum va plasat a componenta respectiv a. Aceste constr angeri vor specicate prin intermediul unui obiect de tip GridBagConstraints, care poate refolosit pentru mai multe componente care au acelea si constr angeri de a sare: gridBag.setConstraints(componenta1, c); gridBag.setConstraints(componenta2, c); . . . Cele mai utilizate tipuri de constr angeri pot specicate prin intermediul urm atoarelor variabile din clasa GridBagConstraints: gridx, gridy - celula ce reprezint a colt ul st anga sus al componentei; gridwidth, gridheight - num arul de celule pe linie si coloan a pe care va a sat a componenta; fill - folosit a pentru a specica dac a o component a va ocupa ntreg spat iul pe care l are destinat; valorile posibile sunt HORIZONTAL, VERTICAL, BOTH, NONE; insets - distant ele dintre component a si marginile suprafet ei sale de a sare; anchor - folosit a atunci c and componenta este mai mic a dec at suprafat a sa de a sare pentru a fort a o anumit a dispunere a sa: nord, sud, est, vest, etc. weigthx, weighty - folosite pentru distribut ia spat iului liber; uzual au valoarea 1;

216

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

Ca exemplu, s a realiz am o fereastr a ca n gura de mai jos. Pentru a simplica codul, a fost creat a o metod a responsabil a cu setarea valorilor gridx, gridy, gridwidth, gridheight si ad augarea unei componente cu restrict iile stabilite pe fereastr a.

Listing 9.7: Gestionarul GridBagLayout


import java . awt .*; public class TestGridBagLayout { static Frame f ; static GridBagLayout gridBag ; static GridBagConstraints 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 GridBagConstraints () ; gbc . weightx = 1.0; gbc . weighty = 1.0;

9.3. GESTIONAREA POZIT IONARII


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 = GridBagConstraints . BOTH ; adauga ( mesaj , 0 , 0 , 4 , 2) ; Label etNume = new Label ( " Nume : " ) ; gbc . fill = GridBagConstraints . NONE ; gbc . anchor = GridBagConstraints . 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 = GridBagConstraints . HORIZONTAL ; gbc . anchor = GridBagConstraints . 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 = GridBagConstraints . NONE ; adauga ( adaugare , 3 , 2 , 1 , 2) ; Button salvare = new Button ( " Salvare " ) ; gbc . fill = GridBagConstraints . HORIZONTAL ; adauga ( salvare , 1 , 4 , 1 , 1) ; Button iesire = new Button ( " Iesire " ) ; adauga ( iesire , 2 , 4 , 1 , 1) ; f . pack () ; f . show () ; } }

218

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

9.3.7

Gruparea componentelor (Clasa Panel)

Plasarea componentelor direct pe suprafat a de a sare poate deveni incomod a n cazul n care avem multe obiecte grace. Din acest motiv, se recomand a gruparea componentelor nrudite ca funct ii astfel nc at s a putem siguri c a, indiferent de gestionarul de pozit ionare al suprafet ei de a sare, ele se vor g asi mpreun a. Gruparea componentelor se face n panel-uri. Un panel este cel mai simplu model de container. El nu are o reprezentare vizibil a, rolul s au ind de a oferi o suprafat a de a sare pentru componente grace, inclusiv pentru alte panel-uri. Clasa care instant iaza aceste obiecte este Panel, extensie a superclasei Container. Pentru a aranja corespunz ator componentele grupate ntr-un panel, acestuia i se poate specica un gestionar de pozit ionare anume, folosind metoda setLayout. Gestionarul implicit pentru containerele de tip Panel este FlowLayout. A sadar, o aranjare ecient a a componentelor unei ferestre nseamn a: gruparea componentelor nfrat ite (care nu trebuie s a e despart ite de gestionarul de pozit ionare al ferestrei) n panel-uri; aranjarea componentelor unui panel, prin specicarea unui gestionar de pozit ionare corespunz ator; aranjarea panel-urilor pe suprafat a ferestrei, prin specicarea gestionarului de pozit ionare 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 act iune a utilizatorului asupra unei componente grace si reprezint a mecanismul prin care utilizatorul comunic a efectiv cu programul. Exemple de evenimente sunt: ap asarea unui buton, modicarea textului ntr-un control de editare, nchiderea sau redimensionarea unei ferestre, etc. Componentele care genereaz a anumite evenimente se mai numesc si surse de evenimente. Interceptarea evenimentelor generate de componentele unui program se realizeaz a prin intermediul unor clase de tip listener (ascult ator, consumator de evenimente). In Java, orice obiect poate consuma evenimentele generate de o anumit a component a grac a.

A sadar, pentru a scrie cod care s a se execute n momentul n care utilizatorul interactioneaz a cu o component a grac a trebuie s a facem urm atoarele lucruri: s a scriem o clas a de tip listener care s a asculte evenimentele produse de acea component a si n cadrul acestei clase s a implement am metode specice pentru tratarea lor;

220

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

s a comunic am componentei surs a c a respectiva clasa i ascult a evenimentele pe care le genereaz a, cu alte cuvinte s a nregistr am acea clas a drept consumator al evenimentelor produse de componenta respectiv a. Evenimentele sunt, ca orice altceva n Java, obiecte. Clasele care descriu aceste obiecte se mpart n mai multe tipuri n funct ie de componenta care le genereaz a, mai precis n funct ie de act iunea utilizatorului asupra acesteia. Pentru ecare tip de eveniment exist a o clas a care instant iaz a obiecte de acel tip. De exemplu, evenimentul generat de act ionarea 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 a ind prezentat a ulterior. O clas a consumatoare de evenimente (listener) poate orice clas a care specica n declarat ia sa c a dore ste s a asculte evenimente de un anumit tip. Acest lucru se realizeaz a prin implementarea unei interfet e specice ec arui tip de eveniment. Astfel, pentru ascultarea evenimentelor de tip ActionEvent clasa respectiv a trebuie s a implementeze interfat a ActionListener, pentru TextEvent interfat a care trebuie implementata este TextListener, etc. Toate aceste interfet e sunt derivate din EventListener. Fiecare interfat a dene ste una sau mai multe metode care vor apelate automat la aparit ia 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 ... } } Intruc at o clas a poate implementa oric ate interfet e, ea va putea s a asculte 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 n continuare metodele ec arei interfet e pentru a sti ce trebuie s a implementeze o clas a consumatoare de evenimente. A sa cum am spus mai devreme, pentru ca evenimentele unei componente s a e interceptate de c atre o instant a a unei clase ascult ator, aceast a clas a trebuie nregistrata n lista ascult atorilor componentei respective. Am spus lista, deoarece evenimentele unei componente pot ascultate de oric ate clase, cu condit ia ca acestea s a e nregistrate la componenta respectiv a. Inregistrarea unei clase n lista ascult atorilor unei componente se face cu metode din clasa Component de tipul addTipEvenimentListener, iar eliminarea ei din aceast a list a cu removeTipEvenimentListener.

Sumariz and, tratarea evenimentelor n Java se desf a soar a astfel: Componentele genereaz a evenimente c and ceva interesant se nt ampl a; Sursele evenimentelor permit oric arei clase s a asculte evenimentele sale prin metode de tip addXXXListener, unde XXX este un tip de eveniment; O clas a care ascult a evenimente trebuie s a implementeze interfet e specice ec arui tip de eveniment - acestea descriu metode ce vor apelate automat la aparit ia evenimentelor.

9.4.1

Exemplu de tratare a evenimentelor

Inainte de a detalia aspectele prezentate mai sus, s a consider am un exemplu de tratare a evenimentelor. Vom crea o fereastr a care s a cont in a dou a butoane cu numele OK, repectiv Cancel. La ap asarea ec arui buton vom scrie pe bara de titlu a ferestrei mesajul Ati apasat butonul .... Listing 9.9: Ascultarea evenimentelor a dou a butoane
import java . awt .*; import java . awt . event .*;

222

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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 a denim clase speciale pentru ascultarea evenimentelor. In exemplul de mai sus am denit clasa Ascultator pentru a intercepta evenimentele produse de cele dou a butoane si din acest motiv a trebuit s a trimitem ca parametru constructorului clasei o referint a la fereastra noastr a. Mai simplu ar fost s a folosim chiar clasa Fereastra pentru a trata evenimentele produse de componentele sale. Vom modica put in si

9.4. TRATAREA EVENIMENTELOR

223

aplicat ia pentru a pune n evident a o alt a modalitate de a determina componenta generatoare a unui eveniment - metoda getSource. Listing 9.10: Tratarea evenimentelor n ferestr 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

A sadar, orice clas a poate asculta evenimente de orice tip cu condit ia s a implementeze interfet ele specice acelor tipuri de evenimente.

9.4.2

Tipuri de evenimente

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

Evenimentele de nivel jos reprezint a o interact iune de nivel jos cum ar o ap asare de tast a, mi scarea mouse-ului, sau o operat ie asupra unei ferestre. In tabelul de mai jos sunt enumerate clasele ce descriu aceste evenimente si operat iunile efectuate (asupra unei componente) care le genereaz a: ComponentEvent Ascundere, deplasare, redimensionare, a sare ContainerEvent Ad augare pe container, eliminare FocusEvent Obt inere, pierdere foucs KeyEvent Ap asare, eliberare taste, tastare MouseEvent Operat iuni cu mouse-ul: click, drag, etc. WindowEvent Operat iuni asupra ferestrelor: minimizare, maximizare,etc. O anumit a act iune a utilizatorului poate genera mai multe evenimente. De exemplu, tastarea literei A va genera trei evenimente: unul pentru ap asare, unul pentru eliberare si unul pentru tastare. In funct ie de necesit a tile aplicat ie putem scrie cod pentru tratarea ec arui eveniment n parte.

Evenimentele semantice reprezint a interact iunea cu o component a GUI: ap asarea unui buton, selectarea unui articol dintr-o list a, etc. Clasele care descriu aceste tipuri de evenimente sunt: ActionEvent AdjustmentEvent ItemEvent TextEvent Act ionare Ajustarea unei valori Schimbarea st arii Schimbarea textului

9.4. TRATAREA EVENIMENTELOR

225

Urm atorul tabel prezint a componentele AWT si tipurile de evenimente generate, prezentate sub forma interfet elor corespunz atoare. Evident, evenimentele generate de o superclas a, cum ar Component, se vor reg asi si pentru toate subclasele sale. Component ComponentListener FocusListener KeyListener MouseListener MouseMotionListener ContainerListener WindowListener ActionListener

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

ItemListener

AdjustmentListener TextListener

Observat i c a de si exist a o singur a clas a MouseEvent, exist a dou a interfet e asociate MouseListener si MouseMotionListener. Acest lucru a fost f acut deoarece evenimentele legate de deplasarea mouse-ului sunt generate foarte frecvent si recept ionarea lor poate avea un impact negativ asupra vitezei de execut ie, n situat ia c and tratarea acestora nu ne intereseaz a si dorim s a trat am doar evenimente de tip click, de exemplu.

Orice clas a care trateaz a evenimente trebuie s a implementeze obligatoriu metodele interfet elor corespunz atoare. Tabelul de mai jos prezint a, pentru ecare interfat a, metodele puse la dispozitie si care trebuie implementate de c atre clasa ascult ator.

226

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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 n care un obiect listener trateaz a evenimente de acela si tip provocate de componente diferite, este necesar s a putem aa, n cadrul uneia din metodele de mai sus, care este sursa evenimentului pe care l trat am pentru a putea react iona n consecint a. Toate tipurile de evenimente mo stenesc metoda getSource care returneaz a obiectul responsabil cu generarea evenimentului. In cazul n care dorim s a diferent iem doar tipul componentei surs 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 l ang a getSource, obiectele ce descriu evenimente pot pune la dispozit ie si alte metode specice care permit aarea de informat ii legate de evenimentul generat. De exemplu, ActionEvent cont ine metoda getActionCommand care, implicit, returneaz a eticheta butonului care a fost ap asat. Astfel de particularit a ti vor prezentate mai detaliat n sect iunile dedicate ec arei componente n parte.

9.4.3

Folosirea adaptorilor si a claselor anonime

Am vazut c a o clas a care trateaz a evenimente de un anumit tip trebuie s a implementeze interfat a corespunz atoare acelui tip. Aceasta nseamn a c a trebuie s a implementeze obligatoriu toate metodele denite de acea interfat a, chiar dac a nu specic a nici un cod pentru unele dintre ele. Sunt ns a situat ii c and acest lucru poate deveni sup arator, mai ales atunci c and nu ne intereseaz a dec at o singura metod a a interfet ei. Un exemplu sugestiv este urm atorul: o fereastr a care nu are specicat cod pentru tratarea evenimentelor sale nu poate nchis a cu butonul standard

228

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

marcat cu x din colt ul dreapta sus si nici cu combinat ia de taste Alt+F4. Pentru a realiza acest lucru trebuie interceptat evenimentul de nchidere a ferestrei n metoda windoClosing si apelat a metoda dispose de nchidere a ferestrei, sau System.exit pentru terminarea programului, n cazul c and este vorba de fereastra principal a a aplicat iei. Aceasta nseamn a c a trebuie s a implement am interfat a WindowListener care are nu mai put in de sapte metode. Listing 9.11: Implementarea interfet ei WindowListener
import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements WindowListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( 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 windowDeactivated ( WindowEvent e ) {} } public class TestWindowListener { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test WindowListener " ) ; f . show () ; } }

Observat i c a trebuie s a implement am toate metodele interfet ei, chiar dac a nu scriem nici un cod pentru unele dintre ele. Singura metod a care ne intereseaz a este windowClosing, n care specic am ce trebuie f acut atunci c and utilizatorul doreste s a nchid a fereastra. Pentru a evita scrierea inutil a a

9.4. TRATAREA EVENIMENTELOR

229

acestor metode, exist a o serie de clase care implementeaz a interfet ele de tip listener f ar a a specica nici un cod pentru metodele lor. Aceste clase se numesc adaptori. Un adaptor este o clas a abstract a care implementeaz a o anumit a interfat a f ar a a specica cod nici unei metode a interfet ei. Scopul unei astfel de clase este ca la crearea unui ascult ator de evenimente, n loc s a implement a o anumit a interfat a si implicit toate metodele sale, s a extindem adaptorul corespunz ator interfet ei respective (dac a are!) si s a supradenim doar metodele care ne intereseaz a (cele n care vrem s a scriem o anumit a secvent a de cod). De exemplu, adaptorul interfet ei WindowListener este WindowAdapter iar folosirea acestuia este dat a n exemplul de mai jos: 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 modalit a ti de tratare a evenimentelor este reducerea codului programului, acesta devenind mult mai lizibil. Ins a exist a si dou a dezavantaje majore. Dup a cum at i observat fat ade exemplul anterior,

230

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

clasa Fereastra nu poate extinde WindowAdapter deoarece ea extinde deja clasa Frame si din acest motiv am construit o nou a clas a numit a Ascultator. Vom vedea ns a c a acest dezavantaj poate eliminat prin folosirea unei clase anonime. Un alt dezavantaj este c a orice gre seal a de sintax a n declararea unei metode a interfet ei nu va produce o eroare de compilare dar nici nu va supradeni metoda interfet ei ci, pur si simplu, va crea o metod a a clasei respective. 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 dat i tot i adaptorii interfet elor de tip listener - se oberv a c a o interfat a XXXListener are un adaptor de tipul XXXAdapter. Interfet ele care nu au un adaptor sunt cele care denesc o singur a metod a si prin urmare crearea unei clase adaptor nu si are rostul. Interfat a 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

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

9.4. TRATAREA EVENIMENTELOR

231

adaptorilor direct n corpul unei clase care cont ine componente ale c aror evenimente trebuie tratate. Listing 9.13: Folosirea adaptorilor si a claselor anonime
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 ste ne r ( new MouseMotionAdapter () { 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA


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 a cum am v azut suprafet ele de a sare ale componentelor sunt extensii 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 si Dialog. O aplicat ie Java cu intefat a grac a va format a din una sau mai multe ferestre, una dintre ele ind numit a fereastra principal a.

9.5.1

Clasa Window

Clasa Window este rar utilizat a n mod direct deoarece permite doar crearea unor ferestre care nu au chenar si nici bar a de meniuri. Este util a atunci c and dorim s a a sam ferestre care nu interact ioneaz a cu utilizatorul ci doar ofer a anumite informat ii. Metodele mai importante ale clasei Window, care sunt de altfel mo stenite de toate subclasele sale, sunt date de mai jos: show - face vizibil a fereastra. Implicit, o fereastr a nou creat a nu este vizibil a; hide - face fereastra invizibil a f ar a a o distruge ns a; pentru a redeveni vizibila se poate apela metoda show;

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

233

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

9.5.2

Clasa Frame

Este derivat a a clasei Window si este folosit a pentru crearea de ferestre independente si funct ionale, eventual contin and o bar a de meniuri. Orice aplicat ie cu interfat a grac a contt ine cel put in o fereastr a, cea mai important a ind numit a si fereastra principal a. Constructorii uzuali ai clasei Frame permit crearea unei ferestre cu sau f ar a titlu, init ial invizibil a. Pentru ca o fereastr a s a devin a vizibil a se va apela metoda show denit a n superclasa Window. import java.awt.*; public class TestFrame { public static void main(String args[]) { Frame f = new Frame("Titlul ferestrei"); f.show(); } } Crearea ferestrelor prin instant ierea direct a a obiectelor de tip Frame este mai put in folosit a. De obicei, ferestrele unui program vor denite n clase separate care extind clasa Frame, ca n exemplul de mai jos: import java.awt.*; class Fereastra extends Frame{ // Constructorul public Fereastra(String titlu) { super(titlu); ... }

234

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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

Gestionarul de pozit ionare implicit al clasei Frame este BorderLayout. Din acest motiv, n momentul n care fereastra este creat a dar nici o component a grac a nu este ad augat a, suprafat a de a sare a feretrei va determinat a automota de gestionarul de pozitt ionare si va oferi doar spat iul necesar a s arii barei ferestrei si grupului de butoane pentru minimizare, maximizare si nchidere. Acela si efect l vom obt ine dac a o redimenionam si apel am apoi metoda pack care determin a dimeniunea suprafet ei de a sare n funct ie de componentele ad augate. Se observ a de asemenea c a butonul de nchidere a ferestrei nu este funct ional. Tratarea evenimentelor ferestrei se face prin implementarea interfet ei WindowListen sau, mai uzual, prin folosirea unui adaptor de tip WindowAdapter. Structura general a a unei ferestre este descris a de clasa Fereastra din exemplul de mai jos: Listing 9.14: Structura general a a unei ferestre
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 . addWindowListener ( 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 l ang a metodele mo stenite din clasa Window, exist a o serie de metode specice clasei Frame. Dintre cele mai folosite amintim: getFrames - metod a static a ce returneaz a lista tuturor ferestrelor deschise ale unei aplicat ii; setIconImage - seteaz a iconit a ferestrei; setMenuBar - seteaz a bara de meniuri a ferestrei (vezi Folosirea meniurilor); setTitle - seteaz a titlul ferestrei;

236

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

setResizable - stabile ste dac a fereastra poate redimenionat a de utilizator;

9.5.3

Clasa Dialog

Toate interfet ele grace ofer a un tip special de ferestre destinate prelu arii unor informat ii sau a unor date de la utilizator. Acestea se numesc ferestre de dialog sau casete de dialog si sunt implementate prin intermediul clasei Dialog, subclas a direct a a clasei Window. Diferent a major a dintre ferestrele de dialog si ferestrele de tip Frame const a n faptul c a o fereastr a de dialog este dependent a de o alt a fereastra (normal a sau tot fereastr a de dialog), numit a si fereastra p arinte. Cu alte cuvinte, ferestrele de dialog nu au o existent a de sine st at atoare. C and fereastra p arinte este distrus a sunt distruse si ferestrele sale de dialog, c and este minimizat a ferestrele sale de dialog sunt f acute invizibile iar c and este restaurat a acestea sunt aduse la starea n care se g aseau n momentul minimiz arii ferestrei p arinte. Ferestrele de dialog pot de dou a tipuri: modale: care blocheaz a accesul la fereastra parinte n momentul deschiderii lor, cum ar ferestrele de introducere a unor date, de alegere a unui sier, de selectare a unei opt iuni, mesaje de avertizare, etc; nemodale: care nu blocheaz a uxul de intrare c atre fereastra p arinte - de exemplu, dialogul de c autare a unui cuv ant ntr-un sier, etc. Implicit, o fereastr a de dialog este nemodal a si invizibil a, ns a exist a constructori care s a specice si ace sti parametri. Constructorii clasei Dialog 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 p arinte reprezint a referint a la fereastra p arinte, titlu reprezint a titlul ferestrei iar prin argumentul modal a specic am dac a fereastra de dialog creat a va modal a (true) sau nemodal a (false - valoarea implicit a). Crearea unei ferestre de dialog este relativ simpla si se realizeaz a prin derivarea clasei Dialog. Comunicarea dintre fereastra de dialog si fereastra sa p arinte, pentru ca aceasta din urm a s a poat a folosi datele introduse (sau opt iunea specicata) n caseta de dialog, se poate realiza folosind una din urm atoarele abord ari generale: obiectul care reprezint a dialogul poate s a trateze evenimentele generate de componentele de pe suprafat a s a si s a seteze valorile unor variabile accesibile ale ferestrei p arinte n momentul n care dialogul este ncheiat; obiectul care creeaz a dialogul (fereastra p arinte) s a se nregistreze ca ascult ator al evenimentelor de la butoanele care determin a ncheierea dialogului, iar fereastra de dialog s a ofere metode publice prin care datele introduse s a e preluate din exterior; S a cre am, de exemplu, o fereastr a de dialog modal a pentru introducerea unui sir de caractere. Fereastra principal a a aplicatiei va p arintele casetei de dialog, va primi sirul de caractere introdus si si va modica titlul ca ind acesta. Deschiderea ferestrei de dialog se va face la ap asarea unui buton al ferestrei principale numit Schimba titlul. Cele dou a ferestre vor ar ata ca n imaginile de mai jos: 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

public FerPrinc ( String titlu ) { super ( titlu ) ; this . addWindowListener ( 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 . addWindowListener ( 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 si un tip de fereastr a de dialog folosit a pentru selectarea unui nume de sier n vederea nc arc arii sau salv arii unui sier: clasa FileDialog, derivat a din Dialog. Instant ele acestei clase au un comportament comun dialogurilor de acest tip de pe majoritatea platformelor de lucru, dar forma n care vor a sate este specic a platformei pe care ruleaz a aplicat ia. Constructorii clasei sunt: FileDialog(Frame parinte)

240

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

FileDialog(Frame parinte, String titlu) FileDialog(Frame parinte, String titlu, boolean mod) Parametrul p arinte reprezint a referint a ferestrei p arinte, titlu reprezint a titlul ferestrei iar prin argumentul mod specic am dac a nc arc am sau salv am un sier; valorile pe care le poate lua acest argument sunt: FileDialog.LOAD - pentru nc arcare, respectiv 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 a sarea sa se face cu show, caseta de dialog va modal a. Dac a a sarea se face cu setVisible(true), atunci va nemodal a. Dup a selectarea unui sier ea va facut a automat invizibil a. Pe l ang a metodele mo stenite de la superclasa Dialog clasa FileDialog mai cont ine metode pentru obt inerea numelui sierului sau directorului selectat getFile, getDirectory, pentru stabilirea unui criteriu de ltrare setFilenameFilter, etc. S a consider am un exemplu n care vom alege, prin intermediul unui obiect FileDialog, un sier cu extensia java. Directorul init ial este directorul curent, iar numele implicit este TestFileDialog.java. Numele sierului ales va a sat la consol 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 . addWindowListener ( 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 a mai rar deoarece n Swing exist a clasa JFileChooser care ofer a mai multe facilit a ti si prin urmare va constitui prima opt iune ntr-o aplicat ie cu intefat a grac a.

242

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

9.6

Folosirea meniurilor

Spre deosebire de celelalte obiecte grace care deriv a din clasa Component, componentele unui meniu reprezint a instant e ale unor clase derivate din superclasa abstract a MenuComponent. Aceast a except ie este facut a deoarece unele platforme grace limiteaz a capabilit a tile unui meniu. Meniurile pot grupate n dou a categorii: Meniuri xe (vizibile permanent): sunt grupate ntr-o bar a de meniuri ce cont ine c ate un meniu pentru ecare intrare a sa. La r andul lor, aceste meniuri cont in articole ce pot selectate, comutatoare sau alte meniuri (submeniuri). O fereastr a poate avea un singur meniu x. Meniuri de context (popup): sunt meniuri invizbile asociate unei ferestre si care se activeaz a uzual prin ap asarea butonului drept al mouse-ului. O alt a diferent a fat a de meniurile xe const a n faptul c a meniurile de context nu sunt grupate ntr-o bar a de meniuri. In gura de mai jos este pus a n evident a alc atuirea unui meniu x:

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

9.6. FOLOSIREA MENIURILOR

243

Pentru a putea cont ine un meniu, o component a trebuie s a implementeze interfat a MenuContainer. Cel mai adesea, meniurile sunt ata sate ferestrelor, mai precis obiectelor de tip Frame, acestea implement and interfat a MenuContainer. Ata sarea unei bare de meniuri la o fereastr a se face prin metoda addMenuBar a clasei Frame.

9.6.1

Ierarhia claselor ce descriu meniuri

S a vedem n continuare care este ierarhia claselor folosite n lucrul cu meniuri si s a analiz am pe r and aceste clase:

Clasa MenuComponent este o clasa abstract a din care sunt extinse toate celelalte clase folosite la crearea de meniuri, ind similar a celeilalte superclase abstracte Component. MenuComponent cont ine metode cu caracter general, dintre care amintim getName, setName, getFont, setFont, cu sintaxa si semnicat iile uzuale.

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

244

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

// 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 a e o instant a a clasei MenuItem. Obiectele acestei clase descriu a sadar opt iunile individuale ale meniurilor derulante, cum sunt Open, Close, Exit, etc. O instant a a clasei MenuItem reprezint a de fapt un buton sau un comutator, cu o anumit a etichet a care va ap area n meniu, nsot it a eventual de un accelerator (obiect de tip MenuShortcut) ce reprezint a combinat ia de taste cu care articolul poate apelat rapid (vezi Acceleratori).

Clasa Menu permite crearea unui meniu derulant ntr-o bar a de meniuri. Opt ional, un meniu poate declarat ca ind tear-o, ceea ce nseamn a c a poate deschis si deplasat cu mouse-ul (dragged) ntr-o alt a pozit ie dec at cea original a (rupt din pozit ia sa). Acest mecanism este dependent de platform a si poate ignorat pe unele dintre ele. Fiecare meniu are o etichet a, care este de fapt numele s au ce va a sat pe bara de meniuri. Articolele dintr-un meniu trebuie s a apart in a clasei MenuItem, ceea ce nseamn a c a pot instant e ale uneia din clasele MenuItem, Menu sau CheckboxMenuItem.

Clasa CheckboxMenuItem implementeaz a ntr-un meniu articole de tip comutator - care au dou a st ari logice (validat/nevalidat), act ionarea articolului determin and trecerea sa dintr-o stare n alta. La validarea unui comutator n dreptul etichetei sale va a sat un simbol grac care indic a acest lucru; la invalidarea sa, simbolul grac respectiv va disp area. Clasa CheckboxMenuItem are aceea si funct ionalitate cu cea a casetelor de validare de tip Checkbox, ambele implement and interfat a ItemSelectable.

9.6. FOLOSIREA MENIURILOR

245

S a vedem n continuare cum ar ar ata un program care construie ste un meniu ca n gura prezentat a anterior:

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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

9.6.2

Tratarea evenimentelor generate de meniuri

La alegerea unei opt iuni dintr-un meniu se genereaz a e un eveniment de tip ActionEvent dac a articolul respectiv este de tip MenuItem, e ItemEvent pentru comutatoarele CheckboxMenuItem. A sadar, pentru a activa opt iunile unui meniu trebuie implementate interfat ele ActionListener sau/ si ItemListener n cadrul obiectelor care trebuie s a specice codul ce va executat la alegerea unei opt iuni si implementate metodele actionPerformed, respectiv itemStatChanged. Fiec arui meniu i putem asocia un obiect receptor diferit, ceea ce u sureaz a munca n cazul n care ierarhia de meniuri este complex a. Pentru a realiza leg atura ntre obiectul meniu si obiectul de tip listener trebuie s a adaug am receptorul n lista de ascult atori a meniului respectiv, ntocmai ca pe orice component a, folosind metodele addActionListener, respectiv addItemListener. A sadar, tratarea evenimentelor generate de obiecte de tip MenuItem este identic a cu tratarea butoanelor, ceea ce face posibil ca unui buton de pe suprafat a de a sare s a i corespund a o opt iune dintr-un meniu, ambele cu acela si nume, tratarea evenimentului corespunz ator ap asarii butonului, sau alegerii opt iunii, f ac andu-se o singur a dat a ntr-o clas a care este nregistrat a ca receptor at at la buton c at si la meniu. Obiectele de tip CheckboxMenuItem tip se g asesc ntr-o categorie comun a cu List, Choice, CheckBox, toate implement and interfat a ItemSelectable si deci tratarea lor va f acut a la fel. Tipul de operatie selectare / deselectare este codicat n evenimentul generat de c ampurile statice ItemEvent.SELECTED si ItemEvent.DESELECTED. 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 ncep and cu AWT 1.1 si sunt implementate prin intermediul clasei PopupMenu, subclas a direct a a clasei Menu. Sunt meniuri invizibile care sunt activate uzual prin ap asarea butonului drept al mouse-ului, ind

248

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

a sate la pozit ia la care se g asea mouse-ul n momentul ap as arii butonului s au drept. Metodele de ad augare a articolelor unui meniu de context sunt mo stenite 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")); A sarea meniului de context se face prin metoda show: popup.show(Component origine, int x, int y) si este de obicei rezultatul ap asarii unui buton al mouse-ului, pentru a avea acces rapid la meniu. Argumentul origine reprezint a componenta fat a de originile c areia se va calcula pozit ia de a sare a meniului popup. De obicei, reprezint a instant a ferestrei n care se va a sa meniul. Deoarece interact iunea cu mouse-ul este dependent a de platforma de lucru, exist a o metod a care determin a dac a un eveniment de tip MouseEvent poate responsabil cu deschiderea unui meniu de context. Aceasta este isPopupTrigger si este denit a n clasa MouseEvent. Pozit ionarea si a sarea meniului este ns a responsabilitatea programatorului. Meniurile de context nu se adaug a la un alt meniu (bar a sau sub-meniu) ci se ata seaz a la o component a (de obicei la o fereastr a) prin metoda add a acesteia. In cazul c and avem mai multe meniuri popup pe care vrem s a le folosim ntr-o fereastr a, trebuie s a le denim pe toate si, la un moment dat, vom ad auga ferestrei meniul corespunz ator dup a care l vom face vizibil. Dup a nchiderea acestuia, vom rupe leg atura ntre fereastr a si meniu prin instruct iunea remove: fereastra.add(popup1); ... fereastra.remove(popup1); fereastra.add(popup2); In exemplul de mai jos, vom crea un meniu de contex pe care l vom activa la ap asarea butonului drept al mouse-ului pe suprafat a ferestrei principale. Tratarea evenimentelor generate de un meniu popup se realizeaz a identic ca 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
} }

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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 a specicarea unor combinat ii de taste numite acceleratori (shortcuts) care s a permit a accesarea direct a, prin intermediul tastaturii, a opt iunilor dintr-un meniu. Astfel, oric arui obiect de tip MenuItem i poate asociat un obiect de tip accelerator, denit prin intermediul clasei MenuShortcut. Singurele combinat ii 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 a prin constructorul obiectelor de tip MenuItem n forma: MenuItem(String eticheta, MenuShortcut accelerator), ca n exemplele 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, n care s a e puse n evidentt a c at mai multe din particularit a tile acestora, precum si modul de tratare a evenimentelor generate.

9.7. FOLOSIREA COMPONENTELOR AWT

251

9.7.1

Clasa Label

Un obiect de tip Label (etichet a) reprezint a o component a pentru plasarea unui text pe o suprafat a de a sare. O etichet a este format a dintr-o singur a linie de text static ce nu poate modicat de c atre utilizator, dar poate 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA


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

} }

9.7.2

Clasa Button

Un obiect de tip Button reprezint a o component a pentru plasarea unui buton etichetat pe o suprafat a de a sare. Textul etichetei este format dintr-o singur a linie.

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 . addWindowListener ( 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 a o component a care se poate g asi n dou a st ari: selectat a sau neselectat a (on/o). Act iunea utilizatorului asupra unui comutator l trece pe acesta n starea complementar a celei n care se g asea. Este folosit pentru a prelua o anumit a opt iune de la utilizator.

254

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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 . addWindowListener ( 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 dene ste un grup de comutatoare din care doar unul poate selectat. Uzual, aceste componente se mai numesc butoane radio. Aceast a clas a nu este derivat a din Component, oferind doar o modalitate de grupare a componentelor de tip Checkbox.

256

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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 . addWindowListener ( 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 . getSelectedCheckbox () ; 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 dene ste o list a de opt iuni din care utilizatorul poate selecta una singur a. La un moment dat, din ntreaga list a doar o singur a opt iune este vizibil a, cea selectat a n momentul curent. O component a Choice este nsot it a de un buton etichetat cu o sageat a vertical a la ap asarea c aruia este a sat a ntreaga sa list a de elemente, pentru ca utilizatorul s a poat a selecta o anumit a opt iune.

258

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA 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 . addWindowListener ( 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 dene ste o list a de opt iuni care poate setat a astfel nc at utilizatorul s a poat a selecta o singur a opt iune sau mai multe. Toate elementele listei sunt vizibile n limita dimensiunilor grace ale componentei.

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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA


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 dene ste o bar a de delare pe vertical a sau orizontal a. Este util a pentru punerea la dispozit ia utilizatorului a unei modalit a ti sugestive de a alege o anumit a valoare dintr-un interval.

Listing 9.26: Folosirea clasei ScrollBar


import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements AdjustmentListener { 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 tList ener ( this ) ; } // Metoda interfetei AdjustmentListener

262

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

public void ad ju stm en tV alu eC han ge 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 ata sarea unor bare de delare (orizontal a ssi/sau vertical a) oric arei componente grace. Acest lucru este util pentru acele componente care nu au implementat a funct ionalitatea de delare automat a, cum ar listele (obiecte din clasa List).

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 . addWindowListener ( 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 . SCROLLBARS_ALWAYS ) ; 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 dene ste un control de editare a textului pe o singur a linie. Este util pentru interogarea utilizatorului asupra unor valori.

264

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA 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 . addWindowListener ( 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 dene ste un control de editare a textului pe mai multe linii. Este util pentru editarea de texte, introducerea unor comentarii, etc .

Listing 9.29: Folosirea clasei TextArea


import java . awt .*;

266

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

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 . addWindowListener ( 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 IC A L _ ON 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 . addActionListener ( 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

CU UTILIZATORUL CAPITOLUL 9. INTERFAT A GRAFICA

Capitolul 10 Desenarea
10.1 Conceptul de desenare

Un program Java care are interfat a grac a cu utilizatorul trebuie s a deseneze pe ecran toate componentele sale care au o reprezentare vizual a. Aceast a desenare include componentele standard folosite n aplicat ie precum si cele denite de c atre programator. Desenarea componentelor se face automat si este un proces care se execut a n urm atoarele situat ii: la a sarea pentru prima dat a a unei componente; la operat ii de minimizare, maximizare, redimensionare a suprafet ei de a sare; ca r aspuns al unei solicit ari explicite a programului. Metodele care controleaz a procesul de desenare se g asesc n clasa Component si sunt urm atoarele: void paint(Graphics g) - Deseneaz a o component a. Este o metod a supradenit a de ecare component a n parte pentru a furniza reprezentarea sa grac a specic a. Metoda este apelat a de ecare dat a c and cont inutul componentei trebuie desenat sau redesenat si nu va apelat a explicit. void update(Graphics g) - Actualizeaz a starea grac a a unei componente. Act iunea acestei metode se realizeaz a n trei pa si: 1. sterge componenta prin supradesenarea ei cu culoarea fundalului; 269

270

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

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

Atent ie Toate desenele care trebuie s a apar a pe o suprafat a de desenare se realizeaz a n metoda paint a unei componente, n general apelat a automat sau explicit cu metoda repaint ori de c ate ori componenta respectiv a trebuie redesenat a. Exist a posibilitatea de a desena si n afara metodei paint, ns a aceste desene se vor pierde la prima operat ie de minimizare, maximizare, redimensionare a suprafet ei de a sare.

10.1.1

Metoda paint

Dup a cum am spus, toate desenele care trebuie s a apar a pe o suprafat a de a sare se realizeaz a n metoda paint a unei componente. Metoda paint este denit a n superclasa Component ns a nu are nici o implementare si, din acest motiv, orice obiect grac care dore ste s a se deseneze trebuie s a o supradeneasc a pentru a- si crea propria sa reprezentare. Componentele standard AWT au deja supradenit a aceast a metod a deci nu trebuie s a ne preocupe desenarea lor, ns a putem modica reprezentarea lor grac a prin crearea unei subclase si supradenirea metodei paint, av and ns a grij a s a apel am si metoda superclasei care se ocup a cu desenarea efectiv a a componentei. In exemplul de mai jos, redenim metoda paint pentru un obiect de tip Frame, pentru a crea o clas a ce instant iaz a ferestre pentru o aplicat ie demonstrativ a ( n colt ul st anga sus este a sat textul Aplicatie DEMO).

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

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

10.1.2

Suprafet e de desenare - clasa Canvas

In afara posibilit a tii de a utiliza componente grace standard, Java ofer a si posibilitatea controlului la nivel de punct (pixel) pe dispozitivul grac, respectiv desenarea a diferite forme grace direct pe suprafat a unei componente. De si este posibil, n general nu se deseneaz a la nivel de pixel direct pe suprafat a ferestrelor sau a altor containere, ci vor folosite clase dedicate acestui scop. In AWT a fost denit un tip special de component a numit a Canvas (p anz a de pictor), al c arei scop este de a extins a pentru a implementa

272

CAPITOLUL 10. DESENAREA

obiecte grace cu o anumit a nf a ti sare. A sadar, Canvas este o clas a generic a din care se deriveaz a subclase pentru crearea suprafet elor de desenare (plan se). Plan sele nu pot cont ine alte componente grace, ele ind utilizate doar ca suprafet e de desenat sau ca fundal pentru animat ie. Desenarea pe o plan sa se face prin supradenirea metodei paint a acesteia. Concret, o plan s a este o suprafat a dreptunghiular a de culoare alb a, pe care se poate desena. Dimensiunile sale implicite sunt 0 si, din acest motiv, este recomandat ca o plan sa s a redeneasca metoda getPreferredSize, eventual si getMinimumSize, getMaximumSize, deoarece acestea vor apelate de c atre gestionarii de pozit ionare. Etapele uzuale care trebuie parcurse pentru crearea unui desen, sau mai bine zis a unei componente cu o anumit a nf a ti sare, sunt: crearea unei plan se de desenare, adic a o subclas a a lui Canvas; redenirea metodei paint din clasa respectiv a; redenirea metodelor getPreferredSize, eventual getMinimumSize, getMaximumSize; ad augarea plan sei pe un container cu metoda add. tratarea evenimentelor de tip FocusEvent, KeyEvent, MouseEvent, ComponentEvent, dac a este cazul. Denirea generic a a unei plan se are urm atorul format: 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 a denim o plan s a pe care desen am un p atrat si cercul s au circumscris, colorate diferite. La ecare click de mouse, vom interschimba cele dou a culori 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 a poat a desena, el trebuie s a obt in a un context grac de desenare pentru suprafat a c areia i apart ine regiunea pe care se va desena. Un context grac este, de fapt, un obiect prin intermediul c aruia putem controla procesul de desenare a unui obiect. In general, desenarea se poate face: pe o port iune de ecran, la imprimant a sau ntr-o zon a virtual a de memorie. Un context grac este specicat prin intermediul unui obiect de tip Graphics primit ca parametru n metodele paint si update. In funct ie de dispozitivul zic pe care se face a sarea (ecran, imprimant a, plotter, etc) metodele de desenare au implement ari interne diferite, transparente utilizatorului. Clasa Graphics pune la dispozit ie metode pentru: primitive grace: desenarea de guri geometrice, texte si imagini stabilirea propriet a tilor contextului grac, adic a stabilirea:

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

275

10.2.1

Propriet a tile contextului grac

La orice tip de desenare parametrii legat i de culoare, font, etc. vor specicat i pentru contextul grac n care se face desenarea si nu vor trimi si ca argumente metodelor respective de desenare. In continuare, enumer am aceste propriet a ti si metodele asociate lor din clasa Graphics. Proprietate Culoarea de desenare 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 n care sunt vizibile desenele) void setClip(Shape s) Modul de desenare void setXorMode(Color c) void setPaintMode(Color c)

10.2.2

Primitive grace

Prin primitive grace ne vom referi n continuare la metodele clasei Graphics, care permit desenarea de guri geometrice si texte. Desenarea textelor se face cu uzual cu metoda drawString care prime ste ca argumente un sir si colt ul din st anga-jos al textului. Textul va desenat cu fontul si culoarea curente ale contextului grac. // Desenam la coordonatele x=10, y=20; drawString("Hello", 10, 20); Desenarea gurilor geometrice se realizeaz a cu urm atoarele metode:

276 Figur a geometric a Linie

CAPITOLUL 10. DESENAREA Metode drawLine drawPolyline Dreptunghi simplu drawRect fillRect clearRect Dreptunghi cu chenar draw3DRect ridicat sau ad ancit fill3DRect Dreptunghi cu colt uri 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 a umplut cu culoarea curent a a contextului de desenare, n timp ce metodele care ncep cu draw vor desena doar conturul gurii respective.

10.3

Folosirea fonturilor

Dup a cum vazut, pentru a scrie un text pe ecran avem dou a posibilit a ti. Prima dintre acestea este s a folosim o component a orientat a-text, cum ar Label, iar a doua s a apel am la metodele clasei Graphics de desenare a textelor, cum ar drawString. Indiferent de modalitatea aleas a, putem specica prin intermediul fonturilor cum s a arate textul respectiv, acest lucru realiz andu-se prin metoda setFont e din clasa Component, e din Graphics. Cei mai important i parametri ce caracterizeaz a un font sunt: Numele fontului: Helvetica Bold, Arial Bold Italic, etc. Familia din care face parte fontul: Helvetica, Arial, etc. Dimensiunea fontului:

n alt imea sa;

Stilul fontului: ngro sat (bold), nclinat (italic);

10.3. FOLOSIREA FONTURILOR Metrica fontului.

277

Clasele care ofer a suport pentru lucrul cu fonturi sunt Font si FontMetrics, n continuare ind prezentate modalit a tile de lucru cu acestea.

10.3.1

Clasa Font

Un obiect de tip Font ncapsuleaz a informat ii despre tot i parametrii unui font, mai put in despre metrica acestuia. Constructorul uzual al clasei este cel care prime ste ca argument numele fontului, dimensiunea si stilul acestuia: 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 n exemplele 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 a uzual astfel: // 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 a de lucru are instalate, la un moment dat, o serie ntreag a de fonturi care sunt disponibile pentru scrierea textelor. Lista acestor fonturi se poate obt ine astfel: Font[] fonturi = GraphicsEnvironment. getLocalGraphicsEnvironment().getAllFonts(); Exemplul urmator a seaz a lista tuturor fonturilor disponibile pe platforma curent a de lucru. Textul ec arui nume de font va scris cu fontul s au corespunz ator.

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 = GraphicsEnvironment . 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 a sarea unui sir cu metoda drawString trebuie s a specic am pozit ia la care s a apar a textul respectiv pe ecran. In momentul n care avem de a sat mai multe siruri consecutiv, sau unele sub altele, trebuie s a calcul am pozit iile lor de a sare n funct ie de lungimea si nalt imea n pixeli a celorlalte texte. Pentru aceasta este folosit a clasa FontMetrics. Un obiect din aceast a clas a se construie ste pornind de la un obiect de tip Font si pune la dispozit ie informat ii despre dimensiunile n pixeli pe care le au caracterele fontului respectiv ntr-un anumit context de desenare. A sadar, un obiect de tip FontMetrics ncapsuleaz a informat ii despre metrica unui font, cu alte cuvinte despre dimensiunile n pixeli ale caracterelor sale. Utilitatea principal a a acestei clase const a n faptul c a permite pozit ionarea precis a a textelor pe o suprafat a de desenare, indiferent de fontul folosit de acestea. Metrica unui font const a n urm atoarele atribute pe care le au caracterele sale: Linia de baz a: este linia dup a care sunt aliniate caracterele unui font; Linia de ascendent a: linia superioara pe care nu o depaseste nici un caracter din font Linia de descendent a: linia inferioar a sub care nu coboar a nici un caracter din font; Ascendentul: distant a ntre linia de baz a si linia de ascendent a; Descendentul: distant a ntre linia de baz a si linia de descendent a; L a timea: l a timea unui anumit caracter din font; Distant a ntre linii (leading): distant a optim a ntre dou a linii de text scrise cu acela si font. In alt imea: distant a dintre liniile de baz a (leading+ascent+descent); Figura de mai jos prezint a o imagine reprezentativ a asupra metricii unui font:

280

CAPITOLUL 10. DESENAREA

Reamintim c a la metoda drawString(String s, int x, int y) argumentele x si y repreznit a colt ul din st anga-jos al textului. Ca s a m mai preci si, y reprezint a pozit ia liniei de baz a a textului care va scris. Un context grac pune la dispozit ie o metod a special a getFontMetrics 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 a n alt imea unei linii pe care vor scrise caractere ale unui font; stringWidth - determin a l a timea total a n pixeli a unui sir de caractere specicat; charWidth - determin a l a timea unui anumit caracter din font. In exemplul urm ator sunt a sate pe ecran zilele s apt am anii si lunile anului:

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 a prin combinat ia culorilor standard ro su (red), verde (green) si albastru (blue), la care se adaug a un anumit grad de transparent a (alpha). Fiecare din ace sti patru parametri poate varia ntr-un interval cuprins e ntre 0 si 255 (dac a dorim s a specic am valorile prin numere ntregi), e ntre 0.0 si 1.0 (dac a dorim s a specic am valorile prin numere reale). O culoare este reprezentat a printr-o instant a a clasei Color sau a subclasei sale SystemColor. Pentru a crea o culoare avem dou a posibilit a ti: S a folosim una din constantele denite n cele dou a clase; S a folosim unul din constructorii clasei Color. S a vedem mai nt ai care sunt constantele denite n aceste clase:

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

Observat i c a n clasa Color sunt denite culori uzuale din paleta standard de culori, n timp ce n clasa SystemColor sunt denite culorile componentelor standard (ferestre, texte, meniuri, etc) ale platformei curente de lucru. Folosirea acestor constante se face ca n exemplele de mai jos: Color rosu = Color.red; Color galben = Color.yellow; Color fundal = SystemColor.desktop; Dac a nici una din aceste culori predenite nu corespunde preferint elor noastre, 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 ro su, verde, albastru si transparent a iar parametrul rgb de la ultimul constructor reprezint a un ntreg format din: bit ii 16-23 ro su, 8-15 verde, 0-7 albastru. 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 a faptul c a respectiva culoare este complet opac a, iar valoarea 0 (sau 0.0) specic a transparent a total a. Implicit, culorile sunt complet opace.

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 a o noua versiune a culorii curente mai deschis a, respectiv mai nchis a Determin a parametrii din care este alcatuit a culoarea Determin a valoarea ce reprezint a culoarea respectiv a (bit ii 16-23 ro su, 8-15 verde, 0-7 albastru)

S a consider am o aplicat ie cu ajutorul c areia putem vizualiza dinamic culorile obt inute prin diferite combinat ii ale parametrilor ce formeaz a o culoare. Aplicat ia va ar ata astfel:

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 AdjustmentListener { 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 tment List ener ( this ) ; gValue . add Adjus tment List ener ( this ) ; bValue . add Adjus tment List ener ( this ) ; aValue . add Adjus tment List ener ( this ) ;

} public void ad ju stm en tV alu eC han 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 a folosirea imaginilor create extern n format gif sau jpeg. Orice imagine va reprezentat a ca o instant a a clasei Image. Aceasta nu este o clas a de componente (nu extinde Component) ci implementeaz a obiecte care pot desenate pe suprafat a unor componente cu metode specice unui context grac pentru componenta respectiva (similar modului cum se deseneaz a o linie sau un cerc).

10.5.1

A sarea imaginilor

A sarea unei imagini presupune realizarea urm atoarilor doi pa si: 1. Crearea unui obiect de tip Image; 2. A sarea propriu-zis a ntr-un context grac; Crearea unui obiect de tip Image se face folosind o imagine dintr-un sier e aat pe ma sina pe care se lucreaz a, e aat la o anumit a adres a Web (URL). Metodele pentru nc arcarea unei imagini dintr-un sier se g asesc n clasele Applet si Toolkit, av and ns a aceea si denumire getImage si urm atoarele formate: Applet Toolkit getImage(URL url) getImage(URL url) getImage(URL url, String fisier) getImage(String fisier) Pentru a obt ine un obiect de tip Toolkit se va folosi metoda getDefaultToolkit, ca n exemplul de mai jos: 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 a dac a sierul sau adresa specicata reprezint a o imagine valid a si nici nu ncarc a efectiv imaginea n memorie, aceste operat iuni ind f acute abia n momentul n care se va realiza a sarea imaginii pentru prima dat a. Metoda nu face dec at s a creeze un obiect de tip Image care face referint a la o anumit a imagine extern a.

A sarea unei imagini ntr-un context grac se realizeaz a prin intermediul metodei drawImage din clasa Graphics si, n general, va facut a n metoda 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 a imaginea; x, y sunt coordonatele st anga-sus la care va a sat a imaginea, relative la spat iul de coordonate al contextului grac; observer este un obiect care observ a nc arcarea imaginii si va informat pe m asura derul arii acesteia; width, heigth reprezint a nalt imea si l a timea la care trebuie scalat a imaginea (dac a lipsesc, imaginea va a sat a la dimensiunile ei reale); bgColor reprezint a culoarea cu care vor colorat i pixelii transparent i ai imaginii (poate s a lipseasc a). In exemplul urm ator a sam aceea si imagine de trei ori, folosind forme 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 a true dac a imaginea a fost a sat a n ntregime si false n caz contrar, cu alte cuvinte metoda nu a stept a ca o imagine s a e complet a sat a ci se termin a imediat ce procesul de a sare a nceput. In sect iunea urm atoare vom detalia acest aspect.

10.5.2

Monitorizarea nc arc arii imaginilor

In cazul n care se a seaz a o imagine care se g ase ste pe Internet sau imaginea a sata este de dimensiuni mari se va observa c a aceasta nu apare complet de la nceput ci este desenat a treptat, f ar a intervent ia programatorului. Acest lucru se nt ampl a deoarece metoda drawImage nu face dec at s a declan seze procesul de nc arcare si desenare a imaginii, dup a care red a imediat controlul apelantului, lucru deosebit de util ntruc at procesul de nc arcare a unei imagini poate dura mult si nu este de dorit ca n acest interval de timp (p an a la nc arcarea complet a a imaginii) aplicat ia s a e blocat a. Ca urmare, la apelul metodei drawImage va desenat a numai port iunea de imagine care este disponibil a la momentul init ial si care poate incomplet a. De aceea trebuie s a existe un mecanism prin care componenta s a e redesenat a automat n momentul n care au mai sosit informat ii legate de imagine, p an a la a sarea sa complet a. Acest mecanism este realizat prin intermediul interfet ei ImageObserver, implementat a de clasa Component si deci de toate componentele. Aceast a interfat a descrie obiecte care au nceput s a utilizeze o imagine incomplet a si care trebuie anunt ate de noile date obt inute n legatur a cu imaginea respectiv a. Interfat a ImageObserver are o singur a metod a numit a imageUpdate, ce va apelat a periodic de rul de execut ie (creat automat) care se ocup a cu nc arcarea imaginii. Formatul acestei metode este: boolean imageUpdate (Image img, int flags, int x, int y, int w, int h ) Implementarea implicit a const a dintr-un apel la metoda repaint pentru dreptunghiul specicat la apel si care reprezint a zona din imagine pentru care se cunosc noi informat ii. Intregul f lags furnizeaz a informat ii despre starea transferului. Aceste informat ii pot aate prin intermediul constantelor denite de interfat a:

290 ABORT

CAPITOLUL 10. DESENAREA Inc arcarea imaginii a fost ntrerupt a, nainte de completarea sa ALLBITS Imaginea a fost ncarcat a complet ERROR A ap arut o eroare n timpul nc arc arii imaginii FRAMEBITS Tot i bit ii cadrului curent sunt disponibili HEIGHT In alt imea imaginii este disponibil a PROPERTIES Propriet a tile imaginii sunt disponibile SOMEBITS Au fost recept ionat i noi pixeli ai imaginii WIDTH L a timea imaginii este disponibil a

Prezent a n parametrul f lags a unui bit de valoare 1 pe pozit ia reprezentata de o constant a nseamn a c a respectiva condit ie este ndeplinit a. // Imaginea este completa (flags & ALLBITS) != 0 // Eroare sau transferul imaginii a fost intrerupt (flags & ERROR | ABORT ) != 0 Metoda imageUpdate poate redent a de o component a pentru a personaliza procesul de a sare al imaginii. Aceasta va apelat a apelat a asincron de ecare dat a c and sunt disponibili noi pixeli. 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 a c a metodele clasei Image pentru determinarea dimensiunilor unei imagini au ca argument un obiect de tip ImageObserver. int getHeight(ImageObserver observer) int getWidth(ImageObserver observer) Dac a desenarea se face folosind clasa Canvas, atunci argumentul observer al metodelor referitoare la imagini va this.

10.5. FOLOSIREA IMAGINILOR

291

10.5.3

Mecanismul de double-buering

Tehnica de double-buering implic a realizarea unui desen n memorie si apoi transferul s au pe ecran, pentru a elimina efectul nepl acut de clipire (ickering) rezultat atunci c and sunt efectuate redesen ari repetate la intervale mici de timp. O situat ie frecvent a n care se apeleaz a la double-buering este crearea de animat ii. Secvent a general a de implementare a mecanismului de double-buering este urm atoarea: // 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 n format JPEG

Pachetul com.sun.image.codec.jpeg din distribut ia standard Java ofer a suport pentru salvarea unei imagini aate n memorie ntr-un sier n for-

292

CAPITOLUL 10. DESENAREA

mat JPEG. O clas a responsabil a cu realizarea acestei operat iuni ar putea denit a astfel: 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 n memorie

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

10.6. TIPARIREA

293

vector de numere ntregi n care vom scrie valorile ntregi (RGB) ale culorilor pixelilor ce denesc imaginea noastr a. Dimensiunea vectorului va n alt imea nmult it a cu l a timea n pixeli a imaginii. Constructorul clasei MemoryImageSource este: MemoryImageSource(int w, int h, int[] pixeli, int off, int scan) unde: w, h reprezint a dimensiunile imaginii (l a timea si n alt imea); pixeli[] este vectorul cu culorile imaginii; of f, scan reprezint a modalitatea de construire a matricii imaginii pornind 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 si o vom a sa pe ecran: 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

Tip arirea

Tip arirea n Java este tratat a n aceea si manier a ca si desenarea, singurul lucru diferit ind contextul grac n care se execut a operat iile. Pachetul care ofer a suport pentru tip arire este java.awt.print, iar clasa principal a care controleaz a procesul de tip arire este PrinterJob. O aplicat ie va apela metode ale acestei clase pentru:

294

CAPITOLUL 10. DESENAREA

Crearea unei sesiuni de tip arire (job); Invocarea dialogului cu utilizatorul pentru specicarea unor parametri legat i de tip arire; Tip arirea efectiv a. Orice component a care poate a sat a pe ecran poate si tip arit a. In general, orice informat ii care trebuie at at a sate c at si tip arite, vor ncapsulate ntr-un obiect grac - component a, care are o reprezentare vizual a descris a de metoda paint si care va specica si modalitatea de reprezentare a sa la imprimant a. Un obiect care va tip arit trebuie s a implementeze interfat a Printable, care cont ine o singur a metod a print, responsabil a cu descrierea modalit a tii de tip arire a obiectului. In cazul c and imaginea de pe ecran coincide cu imaginea de la imprimant a, metodele paint si print pot specica aceea si secvent a de cod. In general, metoda print are urm atorul format: 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; } Pa sii care trebuie efectuat i pentru tip arirea unui obiect sunt: 1. Crearea unei sesiuni de tip arire: PrinterJob.getPrinterJob 2. Specicarea obiectului care va tip arit: setPrintable; acesta trebuie s a implementeze interfat a Printable; 3. Opt ional, init ierea unui dialog cu utilizatorul pentru precizarea unor parametri legat i de tip arire: printDialog;

10.6. TIPARIREA 4. Tip arirea efectiv a: print.

295

In exemplul urm ator vom deni un obiect care are aceea si reprezentare pe ecran c at si la imprimant a (un cerc circumscris unui p atrat, nsotite de un text) si vom tip ari obiectul respectiv. Listing 10.6: Tip arirea unei componente
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 . addActionListener ( 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 a variant a pentru tip arirea de texte este deschiderea unui ux c atre dispozitivul special reprezentat de imprimant a si scrierea informat iilor, linie cu linie, pe acest ux. In sistemul de operare Windows, imprimanta poate referit a prin lpt1, iar n Unix prin /dev/lp. Observat i c a aceast a abordare nu este portabil a, deoarece necesit a tratare special a n funct ie de sistemul de operare folosit. Listing 10.7: Tip arirea textelor
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 dispozit ie o serie ntreag a de facilit a ti pentru scrierea de aplicat ii cu o interfat a grac a mult mbog a tit a funct ional si estetic fat a de vechiul model AWT. In JFC sunt incluse urm atoarele: Componente Swing Sunt componente ce nlocuiesc si n acela si timp extind vechiul set oferit de modelul AWT. Look-and-Feel Permite schimbarea nf a ti s arii si a modului de interact iune cu aplicat ia n funct ie de preferint ele ec aruia. Acela si program poate utiliza diverse moduri Look-and-Feel, cum ar cele standard Windows, Mac, Java, Motif sau altele oferite de diver si dezvoltatori, acestea put and interschimbate de c atre utilizator chiar la momentul execut iei . Accessibility API Permite dezvoltarea de aplicat ii care s a comunice cu dispozitive utilizate de c atre persoane cu diverse tipuri de handicap, cum ar cititoare de ecran, dispozitive de recunoa stere a vocii, ecrane Braille, etc. Java 2D API Folosind Java 2D pot create aplicat ii care utilizeaz a grac a la un 299

300

CAPITOLUL 11. SWING nivel avansat. Clasele puse la dispozit ie permit crearea de desene complexe, efectuarea de operat ii geometrice (rotiri, scal ari, translat ii, etc.), prelucrarea de imagini, tip arire, etc.

Drag-and-Drop Ofer a posibilitatea de a efectua operat ii drag-and-drop ntre aplicat ii Java si aplicat ii native. Internat ionalizare Internat ionalizarea si localizarea aplicat iilor sunt dou a facilit a ti extrem de importante care permit dezvoltarea de aplicat ii care s a poat a congurate pentru exploatarea lor n diverse zone ale globului, utiliz and limba si particularit a tile legate de formatarea datei, numerelor sau a monedei din zona respectiv a. In aceste capitol vom face o prezentare scurt a a componentelor Swing, deoarece prezentarea detaliata a tuturor facilit a tilor oferite de JFC ar oferi sucient material pentru un volum de sine st at ator.

11.1.2

Swing API

Unul din principalele deziderate ale tehnologiei Swing a fost s a pun a la dispozit ie un set de componente GUI extensibile care s a permit a dezvoltarea rapid a de aplicat ii Java cu interfat a grac a competitiv a din punct de vedere comercial. Pentru a realiza acest lucru, API-ul oferit de Swing este deosebit de complex av and 17 pachete n care se g asesc sute de clase si interfet e. Lista complet a a pacehetelor din distribut ia standard 1.4 este dat a n tabelul de 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 aplicat ii, cel mai important si care cont ine componentele de baz a ind javax.swing.

Componentele folosite pentru crearea interfet elor grace Swing pot grupate 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

Asem an ari si deosebiri cu AWT

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

302

CAPITOLUL 11. SWING

Aplicat iile GUI vor avea n continuare nevoie de pachetul java.awt deoarece aici sunt denite unele clase utilitare cum ar Color, Font, Dimension, etc. care nu au fost rescrise n Swing. De asemenea, pachetul java.awt.event r am ane n continuare esent ial pentru tratarea evenimentelor generate at at de componente AWT c at si de cele din Swing. Pe l ang a acesta mai poate necesar si javax.swing.event care descrie tipuri de evenimente specice unor componente Swing, mecanismul de tratare a lor ind ns a acela si ca n AWT. Pozit ionarea componentelor este preluat a din AWT, ind ad augate ns a noi clase care descriu gestionari de pozit ionare n completarea celor existente, cum ar BoxLayout si SpringLayout. Difer a ns a modul de lucru cu containere, dup a cum vom vedea n sect iunea dedicat a acestora. Majoritatea componentelor Swing care permit a sarea unui text ca parte a reprezent arii lor GUI pot specica acel text e n mod normal folosind un anumit font si o anumit a culoare ce pot setate cu metodele setFont si setColor, e prin intermediul limbajului HTML. Folosirea HTML aduce o exibilitatea deosebit a n realizarea interfet ei grace, ntruc at putem aplica format ari multiple unui text, descompunerea acestuia pe mai multe linii, etc., singurul dezavantaj ind ncetinirea etapei de a sare a componentelor. JButton simplu = new JButton("Text simplu"); JButton html = new JButton( "<html><u>Text</u> <i>formatat</i></html>"); S a descriem o aplict ie simpl a folosind AWT si apoi Swing, pentru a ne crea o prim a impresie asupra diferent elor si asem an arilor dintre cele dou a modele. Listing 11.1: O aplicat ie simpl a AWT
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: Aplicat ia rescris a folosind Swing


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 a sate pe ecran componentele grace ale unei aplicat ii trebuie plasate pe o suprafat a de a sare (container). Fiecare component a poate cont inut a doar ntr-un singur container, ad augarea ei pe o supraft a nou a de a sare determin and eliminarea ei de pe vechiul container pe care fusese plasat a. Intruc at containerele pot ncapsulate n alte containere, o component a va face parte la un moment dat dintr-o ierarhie. R ad acina acestei ierarhii trebuie s a e un a sa numit container de nivel nalt, care este reprezentat de una din clasele JFrame, JDialog sau JApplet. Intruc at de appleturi ne vom ocupa separat, vom analiza n continuare primele dou a clase. In general orice aplicat ie Java independent a bazat a pe Swing cont ine cel put in un container de nivel nalt reprezentat de fereastra principal a a programului, instant a a clasei JFrame. Simplicat, un obiect care reprezint a o fereastr a Swing cont ine o zon a care este rezervat a barei de meniuri si care este situat a de obieci n partea sa superioar a si corpul ferestrei pe care vor plasate componentele. Imaginea de mai jos pune n evident a aceast a separare, valabil a de altfel pentru orice container de nivel nalt:

Corpul ferestrei este o instant a a clasei Container ce poate obt inut a cu metoda getContentPane. Plasarea si aranjarea componentelor pe suprafat a

11.2. FOLOSIREA FERESTRELOR

305

ferestrei se va face deci folosind obiectul de tip Container si nu direct fereastra. A sadar, de si este derivat a din Frame, clasa JFrame este folosit a ntr-un mod diferit fat a de p arintele s au: 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 a n ascunderea ferestrei atunci c and utilizatorul apas a butonul de nchidere. Acest comportament poate modicat prin apelarea metodei setDefaultCloseOperation care prime ste ca argument diverse constante ce se g asesc e n clasa WindowConstants, e chiar n JFrame. jf.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Ad augarea unei bare de meniuri se realizeaz a cu metoda setJMenuBar, care prime ste o instant a de tip JMenuBar. Crearea meniurilor este similar a cu modelul AWT.

11.2.1

Ferestre interne

Din punctul de vedere al folosirii ferestrelor, aplicat iile pot mp art ite n dou a categorii: SDI (Single Document Interface) MDI (Multiple Document Interface) Programele din prima categorie gestioneaz a la un moment dat o singur a fereastr a n care se g asesc componentele cu care interact ioneaz a utilizatorul. In a doua categorie, fereastra principal a a aplicat iei nglobeaz a la r andul ei alte ferestre, uzual cu funct ionalit a ti similare, ce permit lucrul concurent pe mai multe planuri.

306

CAPITOLUL 11. SWING

In Swing, clasa JInternalFrame pune la dispozit ie o modalitate de a crea ferestre n cadrul altor ferestre. Ferestrele interne au aproximativ aceea si nf a ti sare si funct ionalitate cu ferestrele de tip JFrame, singura diferent a ind 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 a de tip JFrame. Folosirea clasei DesktopPane este necesar a deoarece aceasta stie cum s a gestioneze ferestrele interne, av and n vedere c a acestea se pot suprapune si la un moment dat doar una singur a este activ a. Exemplul urm ator pune n evident a modelul general de creare si a sare a ferestrelor interne: Listing 11.3: Folosirea ferestrelor interne
import javax . swing .*; import java . awt .*; class FereastraPrincipala extends JFrame { public FereastraPrincipala ( String titlu ) { super ( titlu ) ; setSize (300 , 200) ; s e tD e f au l t C lo s e Op e r a ti 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 ar ata ca n gura de mai jos:

11.3

Clasa JComponent

JComponent este superclasa tuturor componentelor Swing, mai put in a celor care descriu containere de nivel nalt JFrame, JDialog, JApplet. Deoarece JComponent extinde clasa Container, deci si Component, ea mo stene ste funct ionalitatea general a a containerelor si componentelor AWT, furniz and bine nt eles si o serie ntreag a de noi facilit a ti. Dintre nout a tile oferite de JComponent amintim: ToolTips Folosind metoda setToolTip poate ata sat unei componente un text cu explicat ii legate de componenta respectiv a. C and utilizatorul trece

308

CAPITOLUL 11. SWING cu mouse-ul deasupra componentei va a sat, pentru o perioad a de timp, textul ajut ator specicat.

Chenare Orice component a Swing poate avea unul sau mai multe chenare. Specicarea unui chenar se realizeaz a cu metoda setBorder.

Suport pentru plasare si dimensionare Folosind metodele setPreferredSize, setMinimumSize, setMaximumSize, setAlignmentX, setAlignmentY pot controlat i parametrii folosit i de gestionarii de pozit ionare pentru plasarea si dimensionarea automat a a componentelor n cadrul unui container. Controlul opacit a tii Folosind metoda setOpaque vom specica dac a o component a trebuie sau nu s a deseneze tot i pixelii din interiorul s au. Implicit, valoarea propriet a tii de opacitate este false, ceea ce nseamn a c a este posibil s a nu e desenat i unii sau chiar tot i pixelii, permit and pixelilor de sub component a s a r am an a vizibili (componenta nu este opac a). Valoarea propriet a tii pentru clasele derivate din JComponent depinde n general de Look-and-Feel-ul folosit. Asocierea de act iuni tastelor Pentru componentele Swing exist a posibilitatea de specica anumite act iuni care s a se execute atunci c and utilizatorul apas a o anumit a combinat ie de taste si componenta respectiv a este activ a (are focusul). Aceast a facilitate simplic a varianta init ial a de lucru, si anume tratarea evenimentelor de tip KeyEvent printr-un obiect KeyListener. Double-Buering Tehnica de double-buering, care implic a desenarea componentei n memorie si apoi transferul ntregului desen pe ecran, este implementat a automat de componentele Swing, spre deosebire de cele AWT unde trebuia realizat a manual dac a era cazul. Exemplul urm ator ilustreaz a modul de folosire a c atorva dintre facilit a tile amintite mai sus: Listing 11.4: Facilit a ti oferite de clasa JComponent

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 at e L o we r e dB e v e lB o r de r () ; raised = BorderFactory . c re a te Ra i se dB e ve lB o rd er () ; title = BorderFactory . createTitledBorder ( " 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 a asem an atoare cu MVC (modelview-controller). Arhitectura MVC specic a descompunerea unei aplicat ii vizuale n trei p art i separate: Modelul - care va reprezenta datele aplicat iei.

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

311

Controlul - transformarea act iunilor utilizatorului asupra componentelor vizuale n evenimente care s a actualizeze automat modelul acestora (datele). Din motive practice, n Swing p art ile de prezentare si control au fost cuplate deoarece exista o leg atur a prea str ans a ntre ele pentru a concepute ca entit a ti separate. A sadar, arhitectura Swing este de fapt o arhitectur a cu model separabil, n care datele componentelor (modelul) sunt separate de reprezentarea lor vizual a. Aceast a abordare este logic a si din perspectiva faptului c a, n general, modul de concepere a unei aplicat ii trebuie s a e orientat asupra reprezent arii si manipul arii informat iilor si nu asupra interfet ei grace cu utilizatorul. Pentru a realiza separarea modelului de prezentare, ec arui obiect corespunz ator unei clase ce descrie o component a Swing i este asociat un obiect care gestioneaz a datele sale si care implementeaz a o interfat a care reprezint a modelul componentei respective. Dup a cum se observ a din tabelul de mai jos, componente cu reprezent ari diferite pot avea acela si tip de model, dar exist a si componente care au asociate mai multe modele: 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 a are un model init ial implicit, ns a are posibilitatea de a-l nlocui cu unul nou atunci c and este cazul. Metodele care acceseaz a

312

CAPITOLUL 11. SWING

modelul unui obiect sunt: setModel, respectiv getModel, cu argumente specice ec arei componente n parte. Crearea unei clase care s a reprezinte un model se va face extinz and interfat a corespunz atoare si implement and metodele denite de aceasta sau extinz and clasa implicit a oferit a de API-ul Swing si supradenind metodele care ne intereseaz a. Pentru modelele mai complexe, cum ar cele asociate claselor JTable, JTree sau JList exist a clase abstracte care implementeaz a interfat a ce descrie modelul respectiv De exemplu, interfat a model a clasei JList este ListModel care este implementat a de clasele DefaultListModel si AbstractListModel. In funct ie de necesit a ti, oricare din aceste clase poate extins a pentru a crea un nou 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 Op e r a ti 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 . addActionListener ( 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 AbstractListModel { public int getSize () { return data1 . length ; } public Object getElementAt ( int index ) { return data1 [ index ]; } } class Model2 extends AbstractListModel { 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 a metode care s a obt in a starea obiectului f ar a a mai nevoie s a obt inem instant a modelului si s a apel am metodele

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 situat ii ns a, mai ales pentru clase cum ar JTable sau JTree, folosirea modelelor aduce exibilitate sporit a programului si este recomandat a utilizarea lor.

11.5.1

Tratarea evenimentelor

Modelele componentelor trebuie s a notice aparit ia unor schimb ari ale datelor gestionate astfel nc at s a poat a reactualizat a prezentarea lor sau s a e executat un anumti cod n cadrul unui obiect de tip listener. In Swing, aceast a noticare este realizat a n dou a moduri: 1. Informativ (lightweight) - Modelele trimit un eveniment prin care sunt informat i ascult atorii c a a survenit o anumit a schimbare a datelor, f ar a a include n eveniment detalii legate de schimbarea survenit a. Obiectele de tip listener vor trebui s a apeleze metode specice componentelor pentru a aa ce anume s-a schimbat. Acest lucru se realizeaz a prin intefat a ChangeListener iar evenimentele sunt de tip ChangeEvent, modelele care suport a aceast a abordare ind BoundedRangeModel, ButtonModel si SingleSelectionModel.
Model BoundedRangeModel ButtonModel SingleSelectionModelModel Listener ChangeListener ChangeListener ChangeListener Tip Eveniment ChangeEvent ChangeEvent ChangeEvent

Interfat a ChangeListener are o singur a metod a: public void stateChanged(ChangeEvent e), singura informat ie cont inut a n eveniment ind componenta surs a. Inregistrarea si eliminarea obiectelor de tip listener se realizeaz a cu metodele 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 u surint a program arii, pentru a nu lucra direct cu instant a modelului, unele clase permit nregistrarea ascult atorilor direct pentru componenta n sine, singura diferent a fat a de varianta anterioar a const and n faptul c a sursa evenimentului este acum de tipul componentei si nu de tipul modelului. Secvent a de cod de mai sus poate rescris a astfel: 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 dispozit ie interfet e specializate si tipuri de evenimente specice ce includ toate informat iile legate de 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 interfet e nu difer a cu nimic de cazul general: 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 a complexit a tii modelului Swing, n aceast a sectt iune nu vom ncerca o abordare exhaustiv a a modului de utilizare a tuturor componentelor, ci vom pune n evident a doar aspectele specice acestui model, subliniind diferent ele si mbun at a tirile fat a AWT.

11.6.1

Componente atomice

In categoria componentelor atomice includem componentele Swing cu funct ionalitate simpl a, a c aror folosire este n general asem an atoare cu a echivalentelor din AWT. Aici includem: Etichete: JLabel Butoane simple sau cu dou a st ari: JButton, JCheckBox, JRadioButton, JToggleButton; mai multe butoane radio pot grupate folosind clasa ButtonGroup, pentru a permite selectarea doar a unuia dintre ele.

Componente pentru progres si derulare: JSlider, JProgressBar, JScrollB Separatori: JSeparator Deoarece utilizarea acestora este n general facil a, nu vom analiza n parte aceste componente.

11.6.2

Componente pentru editare de text

Componentele Swing pentru a sarea si editarea textelor sunt grupate ntr-o ierarhie ce are ca r ad acin a clasa JTextComponent din pachetul javax.swing.tex

11.6. FOLOSIREA COMPONENTELOR

317

Dup a cum se observ a din imaginea de mai sus, clasele pot mp art ite n trei categorii, corespunz atoare tipului textului editat: Text simplu pe o singur a linie JTextField - Permite editarea unui text simplu, pe o singur a linie. JPasswordField - Permite editarea de parole. Textul acestora va ascuns, n locul caracterelor introduse ind a sat un caracter simbolic, cum ar *. JFormattedTextField - Permite introducerea unui text care s a respecte un anumit format, ind foarte util a pentru citirea de numere, date calendaristice, etc. Este folosit a mpreun a cu clase utilitare pentru formatarea textelor, cum ar NumberFormatter, DateFormatter, MaskFormatter, etc. Valoarea cont inut a de o astfel de component a va obt inut a/setat a cu metodele getValue, respectiv setValue si nu cu cele uzuale getText, setText. 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 si nu poate specicat doar unei anumite port iuni. Uzual, o component a de acest tip va inclus a ntr-un container JScrollPane, pentru a permite navigarea pe vertical a

318

CAPITOLUL 11. SWING si orizontal a dac a textul introdus nu ncape n suprafat a alocat a obiectului. Acest lucru este valabil pentru toate componentele Swing pentru care are sens not iunea de navigare pe orizontal a sau vertical a, nici una neoferind suport intrinsec pentru aceast a operat iune.

Text cu stil mbog a tit pe mai multe linii JEditorPane - Permite a sarea si editarea de texte scrise cu stiluri multiple si care pot include imagini sau chiar diverse alet componente. Implicit, urm atoarele tipuri de texte sunt recunoscute: text/plain, text/html si text/rtf. Una din utiliz arile cele mai simple ale acestei clase este setarea documentului ce va a sat cu metoda setPage, ce prime ste ca argument un URL care poate referi un sier text, HTML sau RTF. JTextPane - Aceast a clas a extinde JEditorPane, oferind diverse facilit a ti suplimentare pentru lucrul cu stiluri si paragrafe. Clasa JTextComponent ncearc a s a p astreze c at mai multe similitudini cu clasa TextComponent din AWT, ns a exist a diferent e notabile ntre cele dou a, componenta Swing av and caracteristici mult mai complexe cum ar suport pentru operat ii de undo si redo, tratarea evenimentelor generate de cursor (caret), etc. Orice obiect derivat din JTextComponent este format din: Un model, referit sub denumirea de document, care gestioneaz a starea componentei. O referint a la model poate obt inut a cu metoda getDocument, ce returneaz a un obiect de tip Document. O reprezentare, care este responsabil a cu a sarea textului. Un controller, cunoscut sub numele de editor kit care permite scrierea si citirea textului si care permite denirea de act iuni necesare edit arii. Exist a diferent fat a de AWT si la nivelul trat arii evenimentelor generate 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 ap asarea tastei Enter n c asut a de editare a textului. Interfat a care trebuie implementat a este ActionListener.

11.6. FOLOSIREA COMPONENTELOR

319

CaretEvent - Este evenimentul generat la deplasarea cursorului ce gestioneaz a pozit ia curent a n text. Interfat a corespunz atoare CaretListener cont ine o singur a metod a: caretUpdate ce va apelat a ori de c ate ori apare o schimbare.

DocumentEvent - Evenimentele de acest tip sunt generate la orice schimbare a textului, sursa lor ind documentul (modelul) componentei si nu componenta n sine. Interfat a corespunz atoare este DocumentListener, ce cont ine metodele:

insertUpdate - apelat a la ad augarea de noi caractere; removeUpdate - apelat a dup a o operat iune de stergere; changedUpdate - apelat a la schimbarea unor atribute legate de stilul textului.

PropertyChangeEvent - Este un eveniment comun tuturor componentelor de tip JavaBean, ind generat la orice schimbare a unei propriet a ti a componentei. Interfat a corespunz atoare este PropertyChangeListener, ce cont ine metoda propertyChange.

11.6.3

Componente pentru selectarea unor elemente

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

Clasa JList Clasa JList descrie o list a de elemente dispuse pe una sau mai multe coloane, 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

Init ializarea unei liste se realizeaz a n mai multe modalit a ti: Folosind unul din constructorii care primesc ca argument un vector de elemente.

Object elemente[] = {"Unu", "Doi", new Integer(3), new Double( JList lista = new JList(elemente); Folosind constructorul f ar a argumente si ad aug and apoi elemente modelului 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 a ce trebuie s a implementeze interfat a ListModel, uzual ind folosit a extinderea clasei predenite AbstractListModel si supradenirea metodelor: getElementAt care furnizeaz a elementul de pe o anumit a posit ie din list a, respectiv getSize care trebuie s a returneze num arul total de elemente din list a. Evident, aceast a variant a este mai complex a, oferind exibilitate sporit a n lucrul 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 a se realizeaz a prin intermediul unui model, acesta ind un obiect de tip ListSelectionModel. Obiectele de tip JList genereaz a evenimente de tip ListSelectionEvent, interfat a corespunz atoare ind ListSelectionListener ce cont ine metoda valueChanged apelat a ori de c ate ori va schimbat a select ia elementelor din list 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 a metode pentru selectarea unor elemente din cadrul programului setSelectedIndex, setSelectedIndices, etc. si pentru obt inerea celor selectate la un moment dat getSelectedIndex, getSelectedIndices, etc..

O facilitate extrem de important a pe care o au listele este posibilitatea de a stabili un renderer pentru ecare articol n parte. Implicit toate elementele listei sunt a sate n acela si fel, ns a acest lucru poate schimbat prin crearea unei clase ce implementeaz a interfat a ListCellRenderer si personalizeaz a reprezentarea elementelor listei n funct ie de diver si parametri. Interfat a ListCellRenderer cont ine o singur a metod a getListCellRendererComponent ce returneaz a un obiect de tip Component. Metoda va apelat a n parte pentru reprezentarea ec arui element al listei. 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 a se realizeaz a cu metoda setCellRenderer.

Clasa JComboBox Clasa JComboBox este similar a cu JList, cu deosebirea c a permite doar selectarea unui singur articol, acesta ind si singurul permanent vizibil. Lista

11.6. FOLOSIREA COMPONENTELOR

323

celorlalte elemente este a sat a doar la ap asarea unui buton marcat cu o s ageat a, ce face parte integrant a din component a.

JComboBox funct ioneaz a dup a acelea si principii ca si clasa JList. Init ializarea se face dintr-un vector sau folosind un model de tipul ComboBoxModel, ecare element put and de asemenea reprezentat diferit prin intermediul unui obiect ce implementeaz a aceea si intefat a ca si n cazul listelor: ListCellRenderer. O diferent a notabil a const a n modul de selectare a unui articol, deoarece JComboBox permite si editarea explicit a a valorii elementului, acest lucru ind controlat de metoda setEditable. Evenimentele generate de obiectele JComboBox sunt de tip ItemEvent generate la navigarea prin list a, respectiv ActionEvent generate la selectarea efectiv a a unui articol.

Clasa JSpinner Clasa JSpinner ofer a posibilitatea de a selecta o anumit a valoare (element) dintr-un domeniu prestabilit, lista elementelor neind ns a vizibil a. Este folosit atunci c and domeniul din care poate f acut a select ia este foarte mare sau chiar nem arginit; de exemplu: numere intregi intre 1950 si 2050. Componenta cont ine dou a butoane cu care poate selectat urm atorul, respectiv predecesorul element din domeniu.

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

324

CAPITOLUL 11. SWING

Componentele de acest tip permit si specicarea unui anumit tip de 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 format ari specice. Evenimentele generate de obiectele de tip JSpinner sunt de tip ChangeEvent, generate la schimbarea st arii componentei.

11.6.4

Tabele

Clasa JTable permite crearea de componente care s a a seze o serie de elemente ntr-un format tabelar, articolele ind dispuse pe linii si coloane. Un tabel poate folosit doar pentru a sarea formatat a a unor date, dar este posibil a si editarea informat iei din celulele sale. De asemenea, liniile tabelului pot marcate ca selectate, tipul select iei ind simplu sau compus, tabelele extinz and astfel funct ionalitatea listelor.

De si clasa JTable se g ase ste n pachetul javax.swing, o serie de clase si interfet e necesare lucrului cu tabele se g asesc n pachetul javax.swing.table, acesta trebuind a sadar importat. Init ializarea unui tabel poate f acut a n mai multe moduri. Cea mai simpl a variant a este s a folosim unul din constructorii care primesc ca argumente elementele tabelului sub forma unei matrici sau a unei colect ii de tip Vector si denumirile capurilor de coloan 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 a cum se observ a, tipul de date al elementelor de pe o coloan a este de tip referint a si poate oricare. In cazul n care celulele tabelului sunt

11.6. FOLOSIREA COMPONENTELOR

325

editabile trebuie s a existe un editor potrivit pentru tipul elementului din celula respectiv a. Din motive de ecient a, implementarea acestei clase este orientat a la nivel de coloan a, ceea ce nseamn a c a articole de pe o coloan a vor reprezentate la fel si vor avea acela si tip de editor. A doua variant a de creare a unui tabel este prin implementarea modelului acestuia ntr-o clas a separat a si folosirea constructorului corespunz ator. Interfat a care descrie modelul clasei JTable este TableModel si cont ine metodele care vor interogate pentru obt inerea informat iei din tabel. Uzual, crearea unui model se face prin extinderea clasei predenite AbstractTableModel, care implementeaz a deja TableModel. Tot ceea ce trebuie s a facem este s a supradenim metodele care ne intereseaz a, cele mai utilizate ind (primele trei trebuie obligatoriu supradenite, ele ind declarate abstracte n clasa de baz a): getRowCount - returneaz a num arul de linii ale tabelului; getColumnCount - returneaz a num arul de coloane ale tabelului; getValueAt - returneaz a elementul de la o anumit a linie si coloan a; getColumnName - returneaz a denumirea ec arei coloane; isCellEditable - specic a dac a o anumit a celul a este editabil a. Modelul mai cont ine si metoda setValueAt care poate folosit a pentru setarea explicit a a valorii unei celule. 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 a implement am interfat a TableModelListener ce cont ine metoda tableChanged. Inregistrarea unui listener va f acut a pentru modelul tabelului:

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 a posibilitatea de a selecta una sau mai multe linii, nu neap arat consecutive, gestiunea liniilor selectate ind realizat a prin intermediul unui model. Acesta este o instant a ce implementeaz a, ntocmai ca la liste, interfat a ListSelectionModel. Tratarea evenimentelor generate de schimbarea select iei n tabel se realizeaz a prin nregistrarea unui ascult ator de tip ListSelectionListener: 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 a cum am spus, celule unei coloane vor reprezentare la fel, ecare coloan a av and asociat un obiect renderer responsabil cu crearea componen-

328

CAPITOLUL 11. SWING

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

O situat ie similar a o reg asim la nivelul editorului asociat celulelor dintr-o anumit a coloan a. Acesta este un obiect ce implementeaz a interfat a TreeCellEditor, ce extinde interfat a CellEditor care generalizeaz a conceptul de celul a editabil a pe care l vom mai reg asi la arbori. Implicit, exist a o serie de editoare standard pentru tipurile de date ment ionate anterior, dar este posibil a specicarea unui editor propriu cu metoda setDefaultEditor. Crearea unui editor propriu se realizeaz a cel mai simplu prin extinderea clasei utilitare AbstractCellEditor, care implementeaz a CellEditor, plus implementarea 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 a sarea unor elemente ntr-o manier a ierarhic a. Ca orice component a Swing netrivial a, un obiect JTree reprezint a doar o imagine a datelor, informat ia n sine ind manipulat a prin intermediul unui model. La nivel structural, un arbore este format dintr-o r ad acin a, noduri interne care au cel put in un u si noduri frunz a - care nu mai au nici un descendent.

De si clasa JTree se g ase ste n pachetul javax.swing, o serie de clase si interfet e necesare lucrului cu arbori se g asesc n pachetul javax.swing.tree. Clasa care modeleaz a not iunea de nod al arborelui este DefaultMutableTreeNode, aceasta ind folosit a pentru toate tipurile de noduri. Crearea unui arbore presupune a sadar crearea unui nod (r ad acina), instant ierea unui obiect de tip JTree cu r ad acina creat a si ad augarea apoi de noduri frunz a ca 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 a cum se observ a, nodurile arborelui pot de tipuri diferite, reprezentarea lor implicit a ind obt inut aprin apelarea metodei toString pentru obiectului cont inut. De asemenea, este posibil a specicarea unui text n format HTML ca valoare a unui nod, acesta ind reprezentat ca atare. Dac a varianta ad aug arii explicite a nodurilor nu este potrivit a, se poate implementa o clas a care s a descrie modelul arborelui. Aceasta trebuie s a implementeze intefat a TreeModel. Scopul unei componente de tip arbore este n general selectarea unui nod al ierarhiei. Ca si n cazul listelor sau a tabelelor, gestiunea elementelor selectate se realizeaz a printr-un model, n aceast a situat ie interfat a corespunz atoare ind TreeSelectionModel. Arborii permit nregistrarea unor obiecte listener, de tip TreeSelectionListener, care s a trateze evenimentele generate la schimbarea select iei n arbore. 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 a interfat a TreeCellRenderer, cea folosit a implicit ind DefaultTreeCellRenderer. Prin implementarea interfet ei sau extinderea clasei implicite pot create modalit a ti de personalizare a nodurilor arborelui n funct ie de tipul sau valoarea acestora. Exist a ns a si diverse metode de a schimba nf a ti sarea unui arbore f ar a s a cre am noi clase de tip TreeCellRenderer. Acestea sunt: setRootVisible - Specic a dac a r ad acina e vizibil a sau nu; setShowsRootHandles - Specic a dac a nodurile de pe primul nivel au simboluri care s a permit a expandarea sau restr angerea lor. putClientProperty - Stabile ste diverse propriet a ti, cum ar modul de reprezentare a relat iilor (liniilor) dintre nodurile p arinte si u: tree.putClientProperty("JTree.lineStyle", "Angled"); // sau "Horizontal", "None" Specicarea unei iconit e pentru nodurile frunz a sau interne: 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 a cum stim, containerele reprezint a suprafet de a sare pe care pot plasate ale componente, eventual chiar alte containere. Superclasa componentelor de acest tip este Container, clas a despre care am mai discutat n capitolul dedicat modeluli AWT. Containerele pot mo art ite n dou a categorii: 1. Containere de nivel nalt - Acestea sunt JFrame, JDialog, JApplet si reprezint a r ad acinile ierarhiilor de componente ale unei aplicat ii. 2. Containere intermediare - Reprezint a suprafet e de a sare cu ajutorul c arora pot organizate mai ecient componentele aplicat iei, put and imbricate. Cele mai importante clase care descriu astfel de containere sunt: JPanel JScrollPane JTabbedPane JSplitPane JLayeredPane JDesktopPane JRootPane JPanel are aceea si funct ionalitate ca si clasa Panel din AWT, ind folosit pentru gruparea mai multor componente Swing si plasarea lor mpreun a pe o alt a suprafat a de a sare. Gestionarul de pozit ionare implicit este FlowLayout, acesta put and schimbat ns a chiar n momentul construirii

11.6. FOLOSIREA COMPONENTELOR

333

obiectului JPanel sau ulterior cu metoda setLayout. Ad augarea de componente se realizeaz a ca pentru orice container, folosind metoda add. 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 a foarte important a n arhitectura modelului Swing, deoarece ofer a suport pentru derularea pe orizontal a si vertical a a componentelor a c aror reprezentare complet a nu ncape n suprafat a asociat a, nici o component a Swing neoferind suport intrinsec pentru aceast a operat ie. 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 a pentru suprapunerea mai multor containere, uzual panouri (obiecte de tip JPanel), pe acela si spat iu de a sare, selectarea

334

CAPITOLUL 11. SWING

unuia sau altui panou realiz andu-se prin intermediul unor butoane dispuse pe partea superioar a a componentei, ecare panou av and un astfel de buton corespunz ator. Ca funct ionalitate, ofer a o implementare asem an atoare gestionarului de pozit ionare 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 cont ine dou a componente dispuse e una l ang a cealalt a, e una sub alta si separarea acestora prin intermediul unei bare care s a permit a congurarea suprafet ei alocate ec arei componente. 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 realiz andu-se prin extinderea acesteia, ntocmai ca n modelul AWT. In Swing exist a ns a o serie de clase predenite ce descriu anumite tipuri de dialoguri, extrem de utile n majoritatea aplicat iilor. Acestea sunt: JOptionPane - Permite crearea unor dialoguri simple, folosite pentru a sarea unor mesaje, realizarea unor interog ari de conrmare/renunt are,

336

CAPITOLUL 11. SWING etc. sau chiar pentru introducerea unor valori, clasa ind extrem de congurabil a. Mai jos, sunt exemplicate dou a modalit a ti de utilizare 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 siere si selectarea unui anumit sier pentru operat ii de deschidere, respectiv salvare. JColorChooser - Dialog standard pentru selectarea ntr-o manier a facil a a unei culori. ProgressMonitor - Clas a utilizat a pentru monitorizare a progresului unei operat ii consumatoare de timp.

11.7
11.7.1

Desenarea
Metode specice

Dup a cum stim, desenarea unei componente este un proces care se executa automat ori de c ate ori este necesar. Procesul n sine este asem an ator celui din modelul AWT, ns a exist a unele diferent e care trebuie ment ionate. Orice component a se g ase ste ntr-o ierarhie format a de containere, r ad acina acestei ind un container de nivel nalt, cum ar o fereastr a sau suprafat a unui applet. Cu alte cuvinte, componenta este plasat a pe o suprafat a de a sare, care la r andul ei poate plasat a pe alt a suprafat a si a sa mai departe. C and este necesar a desenarea componentei repsective, e la prima sa a sare, e ca urmare a unor act iuni externe sau interne programului, operat ia de desenare va executat a pentru toate containerele, ncep and cu cel de la nivelul superior.

11.7. DESENAREA

337

Desenarea se bazeaz a pe modelul AWT, metoda cea mai important a ind paint, apelat a automat ori de c ate ori este necesar. Pentru componentele Swing, aceast a metod a are ns a o implementare specic a si nu trebuie supradenit a. Aceasta este responsabil a cu apelul metodelor Swing ce deseneaz a componenta si anume: paintComponent - Este principala metod a pentru desenare ce este supradenit a pentru ecare component a Swing n parte pentru a descrie reprezentarea sa grac a. Implicit, n cazul n care componenta este opac a metoda deseneaz a suprafat a sa cu culoarea de fundal, dup a care va executa desenarea propriu-zis a. paintBorder - Deseneaz a chenarele componentei (dac a exist a). Nu trebuie supradenit a. paintChildren - Solicit a desenarea componentelor cont inute de aceast a component a (dac a exist a). Nu trebuie supradenit a. Metoda paint este responsabil a cu apelul metodelor amintite mai sus si realizarea unor optimiz ari legate de procesul de desenare, cum ar implementarea mecanismului de double-buering. De si este posibil a supradenirea ei, acest lucru nu este recomandat, din motivele amintite mai sus. Ca si n AWT, dac a se dore ste redesenarea explicit a a unei componente se va apela metoda repaint. In cazul n care dimensiunea sau pozit ia componentei s-au schimbat, apelul metodei revalidate va precede apelul lui repaint.

Atent ie Intocmai ca n AWT, desenarea este realizat a de rul de execut ie care se ocup a cu transmiterea evenimentelor. Pe perioada n care acesta este ocupat cu transmiterea unui mesaj nu va f acut a nici o desenare. De asemenea, dac a acesta este blocat ntr-o operat iune de desenare ce consum a mult timp, pe perioada respectiv a nu va transmis nici un mesaj.

338

CAPITOLUL 11. SWING

11.7.2

Considerat ii generale

In continuare vom prezenta c ateva considerat ii generale legate de diferite aspecte ale desen arii n cadrul modelului Swing.

A sarea imaginilor In AWT a sarea unei imagini era realizat a uzual prin supradenirea clasei Canvas si desenarea imaginii n metoda paint a acesteia. In Swing, exist a c ateva solut ii mai simple pentru a sarea unei imagini, cea mai utilizat a ind crearea unei etichete (JLabel) sau a unui buton (JButton) care s a aib a setat a o anumit a imagine pe suprafat a sa. Imaginea respectiv a trebuie creat a folosind clasa ImageIcon. ImageIcon img = new ImageIcon("smiley.gif"); JLabel label = new JLabel(img);

Transparent a Cu ajutorul metodei setOpaque poate controlat a opacitatea componentelor Swing. Aceasta este o facilitate extrem de important a deoarece permite crearea de componente care nu au form a rectangular a. De exemplu, un buton circular va construit ca ind transparent (setOpaque(false)) si va desena n interiorul s au o elips a umplut a cu o anumit a culoare. Evident, este necesar a implementarea de cod specic pentru a trata ap asarea acestui tip de buton. Trabsparent a ns a vine cu un anumit pret , deoarece pentru componentele transparente vor trebui redesenate containerele pe care se g ase ste aceasta, ncetinind astfel procesul de a sare. Din acest motiv, de ecare dat a c and este cazul, se recomand a setarea componentelor ca ind opace (setOpaque(true)).

Dimensiunile componentelor Dup a cum stim, orice component a este denit a de o suprafat a rectangular a. Dimensiunile acestei pot obt inute cu metodele getSize, getWidth, getHeight. Acestea includ ns a si dimsniunile chenarelor, evident dac a acestea exist a. Suprafat a ocupat a de acestea poate aat a cu metoda getInsets

11.7. DESENAREA

339

ce va returna un obiect de tip Insets ce specic a num arul de pixeli ocupat i cu chenare n jurul componentei. 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 a primitivele standard de desenare. In majoritatea cazurilor ns a, argumentul este de fapt de tip Graphics2D, clas a ce extinde Graphics si pune la dispozit ie metode mai sositcate de desenare cunoscute sub numele de Java2D. Pentru a avea acces la API-ul Java2D, este sucient s a facem conversia argumentului 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 si a ilor s ai. Din acest motiv este foarte important ca atunci c and supradenim metoda paintComponent s a ne asigur am c a la terminarea metodei starea obiectului Graphics este aceea si ca la 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 nt elege modul n care sunt desenate componentele Swing si felul n care acestea interact ioneaz a cu utilizatorul. Posibilitatea de a alege ntre diferite moduri L&F are avantajul de a oferi prezentarea unei aplicat ii ntr-o form a grac a care s a corespund a preferint elor utilizatorilor. In principiu, variantele originale de L&F furnizate n distribut ia standard ofereau modalitatea ca o interfat a Swing e s a se ncadreze n ansamblul grac al sistemului de operare folosit, e s a aib a un aspect specic Java. Orice L&F este descris de o clas a derivat a din LookAndFeel. Distribut ia standard Java include urm atoarele clase ce pot utilizate pentru selectarea unui L&F: javax.swing.plaf.metal.MetalLookAndFeel Este varianta implicit a de L&F si are un aspect specic Java. com.sun.java.swing.plaf.windows.WindowsLookAndFeel Varianta specic a sistemelor de operare Windows. Incep and cu versiunea 1.4.2 exist a si implementarea pentru Windows XP . com.sun.java.swing.plaf.mac.MacLookAndFeel Varianta specic a sistemelor de operare Mac. com.sun.java.swing.plaf.motif.MotifLookAndFeel Specic a interfat a CDE/Motif. com.sun.java.swing.plaf.gtk.GTKLookAndFeel GTK+ reprezint a un standard de creare a interfet elor grace dezvoltat independent de limbajul Java. (GTK este acronimul de la GNU Image Manipulation Program Toolkit). Folosind acest L&F este posibil a si

11.8. LOOK AND FEEL

341

specicarea unei anumite teme prin intermediul unui sier de resurse sau folosind variabila swing.gtkthemefile, ca n exemplul de mai jos: java -Dswing.gtkthemefile=temaSpecifica/gtkrc App Specicare unei anumite interfet e L&F poate realizat a prin mai multe modalit a ti.

Folosirea clasei UImanager Clasa UIManager pune la dispozit ie o serie de metode statice pentru selectarea la momentul execut iei a uni anumit L&F, precum si pentru obt inerea unor variante specice: getLookAndFeel - Obt ine varianta curent a, return and un obiect de tip LookAndFeel. setLookAndFeel - Seteaz a modul curet L&F. Metoda prime ste ca argument un obiect dintr-o clas a derivat a din LookAndFeel, e un sir de caractere cu numele complet al clasei L&F. getSystemLookAndFeelClassName - Obt ine variant a specic a sistemului de operare folosit. In cazul n care nu exist a nici o astfel de clas a, returneaz a varianta standard. getCrossPlatformLookAndFeelClassName - Returneaz a interfat a grac a standard Java (JLF). // Exemple: UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());

Setarea propriet a tii swing.defaultlaf Exist a posibilitatea de a specica varianta de L&F a aplicat ie direct de la linia de comand a prin setarea propriet a tii swing.defaultlaf:

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 a variant a de a seta aceast a proprietate este schimbarea ei direct n sierul swing.properties situat n subdirectorul lib al distribut iei Java. # Swing properties swing.defaultlaf= com.sun.java.swing.plaf.windows.WindowsLookAndFeel Ordinea n care este aleas a clasa L&F este urm atoarea: 1. Apelul explicit al metodei UIManager.setLookAndFeel naintea cre arii unei componente Swing. 2. Proprietatea swing.defaultlaf specicat a de la linia de comand a. 3. Proprietatea swing.defaultlaf specicat a n sierul swing.properties. 4. Clasa standard Java (JLF).

Exist a posibilitatea de a schimba varianta de L&F chiar si dup a a sarea componentelor. Acesta este un proces care trebuie s a actualizeze ierarhiile de componente, ncep and cu containerele de nivel nalt si va realizat prin apelul metodei SwingUtilities.updateComponentTreeUI ce va primi ca argument r ad acina unei ierarhii. Secvent a care efectueaz a aceast a operat ie pentru o fereastr a f este: UIManager.setLookAndFeel(numeClasaLF); SwingUtilities.updateComponentTreeUI(f); f.pack();

Capitolul 12 Fire de execut ie


12.1 Introducere

Firele de execut ie fac trecerea de la programarea secvent ial a la programarea concurent a. Un program secvent ial reprezint a modelul clasic de program: are un nceput, o secvent a de execut ie a instruct iunilor sale si un sf ar sit. Cu alte cuvinte, la un moment dat programul are un singur punct de execut ie. Un program aat n execut ie se nume ste proces. Un sistem de operare monotasking, cum ar MS-DOS, nu este capabil s a execute dec at un singur proces la un moment dat, n timp ce un sistem de operare multitasking, cum ar UNIX sau Windows, poate rula oric ate procese n acela si timp (concurent), folosind diverse strategii de alocare a procesorului ec aruia dintre acestea. Am reamintit acest lucru deoarece not iunea de r de execut ie nu are sens dec at n cadrul unui sistem de operare multitasking. Un r de execut ie este similar unui proces secvent ial, n sensul c a are un nceput, o secvent a de execut ie si un sf ar sit. Diferenta dintre un r de execut ie si un proces const a n faptul c a un r de execut ie nu poate rula independent ci trebuie s a ruleze n cadrul unui proces. Denit ie Un r de execut ie este o succesiune scevent ial a de instruct iuni care se execut a n cadrul unui proces.

343

344 Program (proces)

CAPITOLUL 12. FIRE DE EXECUT IE Program (proces)

Un program si poate deni ns a nu doar un r de execut ie ci oric ate, ceea ce nseamn a c a n cadrul unui proces se pot executa simultan mai multe re de execut ie, permit and efectuarea concurent a a sarcinilor independente ale acelui program. Un r de execut ie poate asem anat cu o versiune redus a a unui proces, ambele rul and simultan si independent pe o structur a secvent ial a format a de instruct iunile lor. De asemenea, execut ia simultan a a relor n cadrul unui proces este similar a cu execut ia concurent a a proceselor: sistemul de operare va aloca procesorul dup a o anumit a strategie ec arui r de execut ie p an a la terminarea lor. Din acest motiv rele de execut ie mai sunt numite si procese usoare. Care ar ns a deosebirile ntre un r de execut ie si un proces ? In primul, r and deosebirea major a const a n faptul c a rele de execut ie nu pot rula dec at n cadrul unui proces. O alt a deosebire rezult a din faptul c a ecare proces are propria sa memorie (propriul s au spat iu de adrese) iar la crearea unui nou proces (fork) este realizat a o copie exact a a procesului p arinte: cod si date, n timp ce la crearea unui r nu este copiat dec at codul procesului p arinte, toate rele de execut ie av and acces la acelea si date, datele procesului original. A sadar, un r mai poate privit si ca un context de execut ie n cadrul unui proces. Firele de execut ie sunt utile n multe privint e, ns a uzual ele sunt folosite pentru executarea unor operat ii consumatoare de timp f ar a a bloca procesul principal: calcule matematice, a steptarea eliber arii unei resurse, desenarea componentelor unei aplicat ii GUI, etc. De multe ori ori, rele si desf a soar a activitatea n fundal ns a, evident, acest lucru nu este obligatoriu.

12.2

Crearea unui r de execut ie

Ca orice alt obiect Java, un r de execut ie este o instant a a unei clase. Firele de execut ie denite de o clas a vor avea acela si cod si, prin urmare, aceea si

12.2. CREAREA UNUI FIR DE EXECUT IE

345

secvent a de instruct iuni. Crearea unei clase care s a deneasc a re de execut ie poate facut a prin dou a modalit a ti: prin extinderea clasei Thread. prin implementarea interfet ei Runnable. Orice clas a ale c arei instant e vor executate separat ntr-un r propriu trebuie declarat a ca ind de tip Runnable. Aceasta este o interfat a care cont ine o singur a metod a si anume metoda run. A sadar, orice clas a ce descrie re de execut ie va cont ine metoda run n care este implementat codul ce va rulat. Interfat a Runnable este conceput a ca ind un protocol comun pentru obiectele care doresc s a execute un anumit cod pe durata existent ei lor. Cea mai important a clas a care implementeaz a interfat a Runnable este Thread. Aceasta implementeaz a un r de execut ie generic care, implicit, nu face nimic; cu alte cuvinte, metoda run nu cont ine nici un cod. Orice r de execut ie este o instant a a clasei Thread sau a unei subclase a sa.

12.2.1

Extinderea clasei Thread

Cea mai simpl a metod a de a crea un r de execut ie care s a realizeze o anumit a act iune este prin extinderea clasei Thread si supradenirea metodei run a 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 a clasei este constructorul, care prime ste ca argument un sir ce va reprezenta numele rului de execut ie. In cazul n care nu vrem s a d am nume relor pe care le cre am, atunci putem renunt a la supradenirea

346

CAPITOLUL 12. FIRE DE EXECUT IE

acestui constructor si s a folosim constructorul implicit, f ar a argumente, care creeaz a un r de execut ie f ar a nici un nume. Ulterior, acesta poate primi un nume cu metoda setName. Evident, se pot deni si alt i constructori, ace stia inde utili atunci c and vrem s a trimitem diver si parametri de init ializare rului nostru. A dou a metod a este metoda run, inima oric arui r de execut ie, n care scriem efectiv codul care trebuie s a se execute. Un r de execut ie creat nu este automat pornit, lansarea s a ind realizeaz a de metoda start, denit a n clasa Thread. // Cream firul de executie FirExecutie fir = new FirExecutie("simplu"); // Lansam in executie fir.start(); S a consider am n continuare un exemplu n care denim un r de execut ie ce a seaz a numerele 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 EXECUT IE


// 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

G andind secvent ial, s-ar crede c a acest program va a sa prima dat a numerele de la 0 la 100 cu pasul 5, apoi numerele de la 100 la 200 cu pasul 10, ntruc at primul apel este c atre contorul fir1, deci rezultatul a sat pe ecran ar trbui s a e: 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 ns a, rezultatul obt inut va o intercalare de valori produse de cele dou a re ce ruleaz a simultan. La rul ari diferite se pot obt ine rezultate diferite deoarece timpul alocat ec arui r de execut ie poate s a nu e acela si, el ind controlat de procesor ntr-o manier a aparent aleatoare. Un posibil 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 interfet ei Runnable

Ce facem ns a c and dorim s a cre am o clas a care instant iaz a re de execut ie dar aceasta are deja o superclas a, stiind c a n Java nu este permis a mo stenirea multipl a? class FirExecutie extends Parinte, Thread // incorect ! In acest caz, nu mai putem extinde clasa Thread ci trebuie s a implement am direct interfat a Runnable. Clasa Thread implementeaz a ea ns a si interfat a Runnable si, din acest motiv, la extinderea ei obt ineam implementarea indirect a a interfet ei. A sadar, interfat a Runnable permite unei clase s a e activ a, f ar a a extinde clasa Thread. Interfat a Runnable se g ase ste n pachetul java.lang si este denit a astfel:

348

CAPITOLUL 12. FIRE DE EXECUT IE

public interface Runnable { public abstract void run(); } Prin urmare, o clas a care instant iaz a re de execut ie prin implementarea interfet ei Runnable trebuie obligatoriu s a implementeze metoda run. O astfel de clas a se mai nume ste clas a activ a si are urm atoarea structur a: public class ClasaActiva implements Runnable { public void run() { //Codul firului de executie ... } } Spre deosebire de modalitatea anterioar a, se pierde ns a tot suportul oferit de clasa Thread. Simpla instant iere a unei clase care implemeneaz a interfat a Runnable nu creeaz a nici un r de execut ie, crearea acestora trebuind f acut a explicit. Pentru a realiza acest lucru trebuie s a instant iem un obiect de tip Thread ce va reprezenta rul de execut ie propriu zis al c arui cod se gase ste n clasa noastr a. Acest lucru se realizeaz a, ca pentru orice alt obiect, prin instruct iunea new, urmat a de un apel la un constructor al clasei Thread, ns a nu la oricare dintre ace stia. Trebuie apelat constructorul care s a primeasc a drept argument o instant a a clasei noastre. Dup a creare, rul de execut ie poate lansat printr-un apel al metodei start. ClasaActiva obiectActiv = new ClasaActiva(); Thread fir = new Thread(obiectActiv); fir.start(); Aceste operat iuni pot f acute chiar n cadrul clasei noastre: public class FirExecutie implements Runnable { private Thread fir = null; public FirExecutie() if (fir == null) { fir = new Thread(this);

12.2. CREAREA UNUI FIR DE EXECUT IE fir.start(); } } public void run() { //Codul firului de executie ... } }

349

Specicarea argumentului this n constructorul clasei Thread determin a crearea unui r de execut ie care, la lansarea sa, va apela metoda run din clasa curent a. A sadar, acest constructor accept a ca argument orice instant a a unei clase Runnable. Pentru clasa FirExecutie dat a mai sus, lansarea rului va f acut a automat la instant ierea unui obiect al clasei: FirExecutie fir = new FirExecutie();

Atent ie Metoda run nu trebuie apelat a explicit, acest lucru realiz andu-se automat la apelul metodei start. Apelul explicit al metodei run nu va furniza nici o eroare, ns a aceasta va executat a ca orice alt a metoda, si nu separat ntr-un r.

S a consider am urm atorul folosind interfat a Runnable. anumit tip, pe o suprafat a de re de execut ie care vor rula suprafat a sa.

exemplu n care cre am dou a re de execut ie Fiecare r va desena guri geometrice de un desenare de tip Canvas. Vom porni apoi dou a concurent, desen and guri diferite, ecare pe

Listing 12.2: Folosirea interfet ei Runnable


import java . awt .*; import java . awt . event .*; class Plansa extends Canvas implements Runnable { // Deoarece Plansa extinde Canvas ,

350

CAPITOLUL 12. FIRE DE EXECUT IE

// 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 EXECUT IE


x = ( int ) ( Math . random () * dim . width ) ; y = ( int ) ( Math . random () * dim . height ) ; r = ( int ) ( Math . random () * 100) ; try { Thread . sleep (50) ; } catch ( InterruptedException 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 EXECUT IE

12.3

Ciclul de viat a al unui r de execut ie

Fiecare r de execut ie are propriul s au ciclu de viat a: este creat, devine activ prin lansarea sa si, la un moment dat, se termin a. In continuare, vom analiza mai ndeaproape st arile n care se poate g asi un r de execut ie. Diagrama de mai jos ilustreaz a generic aceste st ari precum si metodele care provoaca tranzit ia dintr-o stare n alta:

A sadar, un r de execut ie se poate g asi n una din urm atoarele patru st ari: New Thread Runnable Not Runnable Dead

Starea New Thread Un r de execut ie se g ase ste n aceast a stare imediat dup a crearea sa, cu alte cuvinte dup a instant ierea unui obiect din clasa Thread sau dintr-o subclas a a sa. Thread fir = new Thread(obiectActiv); // fir se gaseste in starea "New Thread"

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE

353

In aceast a stare rul este vid, el nu are alocate nici un fel de resurse sistem si singura operat iune pe care o putem executa asupra lui este lansarea n execut ie, prin metoda start. Apelul oric arei alte metode n afar a de start nu are nici un sens si va provoca o except ie de tipul IllegalThreadStateException.

Starea Runnable Dup a apelul metodei start un r va trece n starea Runnable, adic a va n execut ie. fir.start(); //fir se gaseste in starea "Runnable" Metoda start realizez a urm atoarele operat iuni necesare rul arii rului de execut ie: Aloc a resursele sistem necesare. Planic a rul de execut ie la procesor pentru a lansat. Apeleaz a metoda run a obiectului activ al rului. Un r aat n starea Runnable nu nseamn a neap arat c a se g a seste efectiv n execut ie, adic a instruct iunile sale sunt interpretate de procesor. Acest lucru se nt ampl a din cauza c a majoritatea calculatoarelor au un singur procesor iar acesta nu poate rula simultan toate rele de execut ie care se gasesc n starea Runnable. Pentru a rezolva aceasta problem a exist a o planicare care s a partajeze dinamic si corect procesorul ntre toate rele de execut ie care sunt n starea Runnable. A sadar, un r care ruleaz a poate s a- si a stepte de fapt r andul la procesor.

Starea Not Runnable Un r de execut ie poate ajunge n aceaat a stare n una din urm atoarele situat ii: Este adormit prin apelul metodei sleep; A apelat metoda wait, a stept and ca o anumit a condit ie s a e satisfacut a;

354

CAPITOLUL 12. FIRE DE EXECUT IE

Este blocat ntr-o operat ie de intrare/ie sire. Metoda sleep este o metod a static a a clasei Thread care provoac a o pauz a n timpul rul arii rului curent aat n execut ie, cu alte cuvinte l adoarme pentru un timp specicat. Lungimea acestei pauze este specicat a n milisecunde si chiar nanosecunde. Intruc at poate provoca except ii de tipul 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) { ... } Observat i c a metoda ind static a apelul ei nu se face pentru o instant a anume a clasei Thread. Acest lucru este foarte normal deoarece, la un moment dat, un singur r este n execut ie si doar pentru acesta are sens adormirea sa. In intervalul n care un r de execut ie doarme, acesta nu va execut chiar dac a procesorul devine disponibil. Dup a expirarea intervalului specicat rul revine n starea Runnable iar dac a procesorul este n continuare disponibil si va continua execut ia. Pentru ecare tip de intrare n starea Not Runnable, exist a o secvent a specic a de ie sire din starea repectiv a, care readuce rul de execut ie n starea Runnable. Acestea sunt: Dac a un r de execut ie a fost adormit, atunci el devine Runnable doar dup a scurgerea intervalului de timp specicat de instruct iunea sleep. Dac a un r de execut ie a steapt a o anumit a condit ie, atunci un alt obiect trebuie s a l informeze dac a acea condit ie este ndeplinit a sau nu; acest lucru se realizeaz a prin instruct iunile notify sau notifyAll (vezi Sincronizarea relor de execut ie). Dac a un r de execut ie este blocat ntr-o operat iune de intrare/ie sire atunci el redevine Runnable atunci c and acea operat iune s-a terminat.

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE

355

Starea Dead Este starea n care ajunge un r de execut ie la terminarea sa. Un r nu poate oprit din program printr-o anumit a metod a, ci trebuie s a se termine n mod natural la ncheierea metodei run pe care o execut a. Spre deosebire de versiunile curente ale limbajului Java, n versiunile mai vechi exista metoda stop a clasei Thread care termina fort at un r de execut ie, ns a aceasta a fost eliminat a din motive de securitate. A sadar, un r de execut ie trebuie s a- si aranjeze singur propria sa moarte.

12.3.1

Terminarea unui r de execut ie

Dup a cum am vazut, un r de execut ie nu poate terminat fort at de c atre program ci trebuie s a- si aranjeze singur terminarea sa. Acest lucru poate realizat n dou a modalit a ti: 1. Prin scrierea unor metode run care s a- si termine execut ia n mod natural. La terminarea metodei run se va termina automat si rul de execut ie, acesta intr and n starea Dead. Ambele exemple anteriorare se ncadreaz a n aceast a categorie. // Primul exemplu public void run() { for(int i = a; i <= b; i += pas) System.out.print(i + " " ); } Dup a a sarea numerelor din intervalul specicat, metoda se termin a si, odat a cu ea, se va termina si rul de execut ie repsectiv. 2. Prin folosirea unei variabile de terminare. In cazul c and metoda run trebuie s a execute o bucl a innit a atunci aceasta trebuie controlat a printr-o variabil a care s a opreasc a ciclul atunci c and dorim ca rul de execut ie s a se termine. Uzual, vom folosi o variabil a membr a a clasei care descrie rul de execut ie care e este public a, e este asociat a cu o metod a public a care permite schimbarea valorii sale. S a consider am exemplul unui r de execut ie care trebuie s a numere secundele scurse p an a la ap asarea tastei Enter.

356

CAPITOLUL 12. FIRE DE EXECUT IE 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 ( InterruptedException 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 a distrugerea explicit a a unui r de execut ie. Sistemul Java de colectare a gunoiului se ocup a de acest lucru. Setarea valorii null pentru variabila care referea instant a rului de execut ie va u sura ns a activitatea procesului gc. Metoda System.exit va oprit fort at toate rele de execut ie si va termina aplicat ia curent a.

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE

357

Pentru a testa dac a un r de execut ie a fost pornit dar nu s-a terminat nc a putem folosi metoda isAlive. Metoda returneaz a: true - dac a rul este n una din st arile Runnable sau Not Runnable false - dac a rul este n una din starile New Thread sau Dead Intre st arile Runnable sau Not Runnable, repectiv New Thread sau Dead nu se poate face nici o diferent iere. 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 execut ie de tip daemon

Un proces este considerat n execut ie dac a cont ine cel put in un r de execut ie activ. Cu alte cuvinte, la rularea unei aplicat ii, ma sina virtual a Java nu se va opri dec at atunci c and nu mai exist a nici un r de execut ie activ. De multe ori ns a dorim s a folosim re care s a realizeze diverse activit a ti, eventual periodic, pe toat a durata de execut ie a programului iar n momentul termin arii acestuia s a se termine automat si rele respective. Aceste re de execut ie se numesc demoni. Dup a crearea sa, un r de execut ie poate f acut demon, sau scos din aceast a stare, cu metoda setDaemon. Listing 12.4: Crearea unui r de excut ie de tip daemon
class Beeper implements Runnable { public void run () { while ( true ) { java . awt . Toolkit . getDefaultToolkit () . beep () ; try { Thread . sleep (1000) ; } catch ( InterruptedException e ) {} }

358
} }

CAPITOLUL 12. FIRE DE EXECUT IE

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 priorit a tilor de execut ie

Majoritatea calculatoarelor au un sigur procesor, ceea ce nseamn a c a rele de execut ie trebuie s a- si mpart a accesul la acel procesor. Execut ia ntr-o anumit a ordine a mai multor re de execut ie pe un num ar limitat de procesoare se nume ste planicare (scheduling). Sistemul Java de execut ie a programelor implementeaz a un algoritm simplu, determinist de planicare, cunoscut sub numele de planicare cu priorit a ti xate. Fiecare r de execut ie Java prime ste la crearea sa o anumit a prioritate. O prioritate este de fapt un num ar ntreg cu valori cuprinse ntre MIN PRIORITY si MAX PRIORITY. Implicit, prioritatea unui r nou creat are n clasa Thread valoarea NORM PRIORITY. Aceste trei constante sunt denite 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 a priorit a tii unui r de execut ie se realizeaz a cu metoda setPriority a clasei Thread. La nivelul sistemului de operare, exist a dou a modele de lucru cu re de execut ie:

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE

359

Modelul cooperativ, n care rele de execut ie decid c and s a cedeze procesorul; dezavantajul acestui model este c a unele re pot acapara procesorul, nepermit and si execut ia altora p an a la terminarea lor. Modelul preemptiv, n care rele de execut ie pot ntrerupte oric and, dup a ce au fost l asate s a ruleze o perioad a, urm and s a e reluate dup a ce si celelalte re aate n execut ie au avut acces la procesor; acest sistem se mai nume ste cu cuante de timp, dezavantajul s au ind nevoia de a sincroniza accesul relor la resursele comune. A sadar, m modelul cooperativ rele de execut ie sunt responsabile cu partajarea timpului de execut ie, n timp ce n modelul preemptiv ele trebuie s a partajeze resursele comune. Deoarece specicat iile ma sinii virtuale Java nu impun folosirea unui anumit model, programele Java trebuie scrise astfel nc at s a funct ioneze corect pe ambele modele. In continuare, vom mai detalia put in aceste aspecte. Planicatorul Java lucreaz a n modul urmator: dac a la un moment dat sunt mai multe re de execut ie n starea Runnable, adic a sunt pregatite pentru a rulate, planicatorul l va alege pe cel cu prioritatea cea mai mare pentru a-l executa. Doar c and rul de execut ie cu prioritate maxim a se termin a, sau este suspendat din diverse motive, va ales un r cu o prioritate mai mic a. In cazul n care toate rele au aceea si prioritate ele sunt alese pe r and, dup a un algoritm simplu de tip round-robin. De asemenea, dac a un r cu prioritate mai mare dec at rul care se execut a la un moment dat solicit a procesorul, atunci rul cu prioritate mai mare este imediat trecut n execut ie iar celalalt trecut n asteptare. Planicatorul Java nu va ntrerupe ns a un r de execut ie n favoarea altuia de aceeasi prioritate, ns a acest lucru l poate face sistemul de operare n cazul n care acesta aloc a procesorul n cuante de timp (un astfel de SO este Windows). A sadar, un r de execut ie Java cedeaz a procesorul n una din situatiile: un r de execut ie cu o prioritate mai mare solicit a procesorul; metoda sa run se termin a; face explicit acest lucru apel and metoda yield; timpul alocat pentru execut ia s a a expirat (pe SO cu cuante de timp).

360

CAPITOLUL 12. FIRE DE EXECUT IE

Atent ie In nici un caz corectitudinea unui program nu trebuie s a se bazeze pe mecansimul de planicare a relor de execut ie, deoarece acesta poate diferit de la un sistem de operare la altul.

Un r de execut ie de lung a durat a si care nu cedeaz a explicit procesorul la anumite intervale de timp astfel nc at s a poata executate si celelalte re de execut ie se nume ste r de execut ie egoist. Evident, trebuie evitat a scrierea lor ntruc at acapareaz a pe termen nedenit procesorul, bloc and efectiv execut ia celorlalte re de execut ie p an a la terminarea sa. Unele sistemele de operare combat acest tip de comportament prin metoda aloc arii procesorului n cuante de timp ec arui r de execut ie, ns a nu trebuie s a ne baz am pe acest lucru la scrierea unui program. Un r de execut ie trebuie s a e corect fat ade celelalte re si s a cedeze periodic procesorul astfel nc at toate s a aib a posibilitatea de a se executa. Listing 12.5: Exemplu de r de execut ie 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 ) ;

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE


s1 . start () ; s2 . start () ; } }

361

Firul de execut ie s1 are prioritate maxim a si p an a nu- si va termina execut ia nu-i va permite rului s2 s a execute nici o instruct iune, acapar and efectiv procesorul. Rezultatul va ar ata astfel: 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 a rul de execut ie curent s a se opreasc a temporar, d and ocazia si altor re s a se execute, e prin adormirea temporar a a rului curent cu ajutorul metodei sleep. Prin metoda yield un r de execut ie nu cedeaz a procesorul dec at relor de execut ie care au aceea si prioritate cu a sa si nu celor cu priorit a ti mai mici. Decoment and linia n care apel am yeld din exemplul anterior, execut ia celor dou a re se va intercala. ... 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 EXECUT IE

12.3.4

Sincronizarea relor de execut ie

P an a acum am v azut cum putem crea re de execut ie independente si asincrone, cu alte cuvinte care nu depind n nici un fel de execut ia sau de rezultatele altor re. Exist a ns a numeroase situat ii c and re de execut ie separate, dar care ruleaz a concurent, trebuie s a comunice ntre ele pentru a accesa diferite resurse comune sau pentru a- si transmite dinamic rezultatele muncii lor. Cel mai elocvent scenariu n care rele de execut ie trebuie s a se comunice ntre ele este cunoscut sub numele de problema produc atorului/consumatorului, n care produc atorul genereaz a un ux de date care este preluat si prelucrat de c atre consumator. S a consider am de exemplu o aplicat ie Java n care un r de execut ie (produc atorul) scrie date ntr-un sier n timp ce alt r de execut ie (consumatorul) cite ste date din acela si sier pentru a le prelucra. Sau, s a presupunem c a produc atorul genereaz a ni ste numere si le plaseaz a, pe r and, ntr-un buer iar consumatorul cite ste numerele din acel buer pentru a le procesa. In ambele cazuri avem de-a face cu re de execut ie concurente care folosesc o resurs a comun a: un sier, respectiv o zon a de memorie si, din acest motiv, ele trebuie sincronizate ntr-o manier a care s a permit a decurgerea normal aa activit a tii lor.

12.3.5

Scenariul produc ator / consumator

Pentru a ntelege mai bine modalitatea de sincronizare a dou a re de execut ie s a implement am efectiv o problem a de tip produc ator/consumator. S a consider am urm atoarea situat ie: Produc atorul genereaz a numerele ntregi de la 1 la 10, ecare la un interval neregulat cuprins ntre 0 si 100 de milisecunde. Pe m asura ce le genereaz a ncearc a s a le plaseze ntr-o zon a de memorie (o variabil a ntreaga) de unde s a e citite de c atre consumator. Consumatorul va prelua, pe r and, numerele generate de c atre produc ator si va a sa valoarea lor pe ecran. Pentru a accesibil a ambelor re de execut ie, vom ncapsula variabila ce va contine numerele generate ntr-un obiect descris de clasa Buffer si care va avea dou a metode put (pentru punerea unui numar n buer) si get (pentru obtinerea numarului din buer).

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE

363

F ar a a folosi nici un mecanism de sincronizare clasa Buffer arat a astfel: Listing 12.6: Clasa Buffer f ar a sincronizare
class Buffer { private int number = -1; public int get () { return number ; } public void put ( int number ) { this . number = number ; } }

Vom implementa acum clasele Producator si Consumator care vor descrie cele dou a re de execut ie. Ambele vor avea o referint a comun a la un obiect de tip Buffer prin intermediul c aruia si comunic a valorile. Listing 12.7: Clasele Producator si Consumator
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 ( InterruptedException e ) { } } } } class Consumator extends Thread { private Buffer buffer ; public Consumator ( Buffer b ) { buffer = b ;

364
}

CAPITOLUL 12. FIRE DE EXECUT IE

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 TestSincronizare1 { 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 a cum ne a steptam, rezultatul rul arii acestui program nu va rezolva nici pe departe problema propus a de noi, motivul ind lipsa oric arei sincroniz ari ntre cele dou a re de execut ie. Mai precis, rezultatul va ceva de 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

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE Producatorul Producatorul Producatorul Producatorul a a a a pus: pus: pus: pus: 6 7 8 9

365

Ambele re de execut ie acceseaz a resursa comun a, adic a obiectul de tip Buffer, ntr-o manier a haotic a si acest lucru se nt ampla din dou motive : Consumatorul nu a steapt a nainte de a citi ca produc atorul s a genereze un num ar si va prelua de mai multe ori acela si num ar. Produc atorul nu a steapt a consumatorul s a preia num arul generat nainte de a produce un altul, n felul acesta consumatorul va rata cu sigurant a unele numere ( n cazul nostru aproape pe toate). Problema care se ridic a n acest moment este: cine trebuie s a se ocupe de sincronizarea celor dou a re de execut ie : clasele Producator si Consumator sau resursa comuna Buffer ? R aspunsul este evident: resursa comun a Buffer, deoarece ea trebuie s a permita sau nu accesul la cont inutul s au si nu rele de execut ie care o folosesc. In felul acesta efortul sincroniz arii este transferat de la produc ator/consumator la un nivel mai jos, cel al resursei critice. Activit a tile produc atorului si consumatorului trebuie sincronizate la nivelul resursei comune n dou a privint e: Cele dou a re de execut ie nu trebuie s a acceseze simultan buer-ul; acest lucru se realizeaz a prin blocarea obiectului Buffer atunci c and este accesat de un r de execut ie, astfel nc at nici nu alt r de execut ie s a nu-l mai poat a accesa (vezi Monitoare). Cele dou a re de execut ie trebuie s a se coordoneze, adic a produc atorul trebuie s a g aseasc a o modalitate de a spune consumatorului c a a plasat o valoare n buer, iar consumatorul trebuie s a comunice produc atorului c a a preluat aceast a valoare, pentru ca acesta s a poat a genera o alta. Pentru a realiza aceasta comunicare, clasa Thread pune la dispozit ie metodele wait, notify, notifyAll. (vezi Semafoare). Folosind sincronizarea clasa Buffer va ar ata astfel:

366

CAPITOLUL 12. FIRE DE EXECUT IE 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 ( InterruptedException e ) { e . printStackTrace () ; } } available = false ; notifyAll () ; return number ; } public synchronized void put ( int number ) { while ( available ) { try { wait () ; // Asteapta consumatorul sa preia valoarea } catch ( InterruptedException 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

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE

367

12.3.6

Monitoare

Denit ie Un segment de cod ce gestioneaz a o resurs a comun a mai multor de re de execut ie separate concurente se nume ste sect iune critic a. In Java, o sect iune critic a poate un bloc de instruct iuni sau o metod a. Controlul accesului ntr-o sect iune critic a se face prin cuv antul cheie synchronized. Platforma Java asociaz a un monitor (lac at) ec arui obiect al unui program aat n execut ie. Acest monitor va indica dac a resursa critic a este accesat a de vreun r de execut ie sau este liber a, cu alte cuvinte monitorizeaz a resursa respectiv a. In cazul n care este accesat a, va pune un lac at pe aceasta, astfel nc at s a mpiedice accesul altor re de execut ie la ea. In momentul c and resursa este eliberat a lac atul va eliminat, pentru a permite accesul altor re de execut ie. In exemplul de tip produc ator/consumator de mai sus, sect iunile critice sunt metodele put si get iar resursa critic a comun a este obiectul buffer. Consumatorul nu trebuie s a acceseze buer-ul c and producatorul tocmai pune o valoare n el, iar produc atorul nu trebuie s a modice valoarea din buer n momentul c and aceasta este citit a de c atre consumator. public synchronized int get() { ... } public synchronized void put(int number) { ... } S a observam c a ambele metode au fost declarate cu modicatorul synchronized. Cu toate acestea, sistemul asociaz a un monitor unei instant e a clasei Buffer si nu unei metode anume. In momentul n care este apelat a o metod a sincronizat a, rul de execut ie care a facut apelul va bloca obiectul a c arei metod a o acceseaz a, ceea ce nseamn a c a celelalte re de execut ie nu vor mai putea accesa resursele critice ale acelui obiect. Acesta este un lucru logic, deoarece mai multe sect iuni critice ale unui obiect gestioneaz a de fapt o singur a resurs a critic a. In exemplul nostru, atunci c and producatorul apeleaz a metoda put pentru a scrie un num ar, va bloca tot obiectul buffer, astfel c a rul de execut ie

368

CAPITOLUL 12. FIRE DE EXECUT IE

consumator nu va avea acces la metoda get, si reciproc. 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 a. De ce s a bloc am toate resursele unui obiect dac a un r de execut ie nu dore ste dec at accesarea uneia sau a c atorva dintre ele ? Deoarece orice obiect are un monitor, putem folosi obiecte ctive ca lac ate pentru ecare din resursele obiectului nostru, ca n exemplul de mai jos: 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 }

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE ... synchronized(xLacat) { synchronized(yLacat) { // Accesam x si y } } ... synchronized(this) { // Accesam x si y } } }

369

Metoda de mai sus nu a fost declarat a cu synchronized ceea ce ar determinat blocarea tuturor resurselor comune la accesarea obiectului respectiv de un r de execut ie, ci au fost folosite monitoarele unor obiecte ctive pentru a controla folosirea ec arei resurs a n parte.

12.3.7

Semafoare

Obiectul de tip Buffer din exemplul anterior are o variabil a membr a privat a numit a number, n care este memorat num arul pe care l comunic a producatorul si pe care l preia consumatorul. De asemenea, mai are o variabil a privat a logic a available care ne d a starea buer-ului: dac a are valoarea true nseamn a c a produc atorul a pus o valoare n buer si consumatorul nu a preluat-o nca; dac a este false, consumatorul a preluat valoarea din buer dar produc atorul nu a pus deocamdat a alta la loc. Deci, la prima vedere, metodele clasei Buffer ar trebui s a arate astfel: 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 EXECUT IE // Nimic - asteptam ca variabila sa devina false } available = true; this.number = number;

} Varianta de mai sus, de si pare corect a, nu este. Aceasta deoarece implementarea metodelor este selsh, cele dou a metode si asteapt a n mod egoist condit ia de terminare. Ca urmare, corectitudinea funct ion arii va depinde de sistemul de operare pe care programul este rulat, ceea ce reprezint a o gre seal a de programare. Punerea corect a a unui r de execut ie n asteptare se realizeaz a cu metoda wait a clasei Thread, care are urm atoarele forme: void wait( ) void wait( long timeout ) void wait( long timeout, long nanos ) Dup a apelul metodei wait, rul de execut ie curent elibereaz a monitorul asociat obiectului respectiv si a steapt a ca una din urm atoarele condit ii s a e ndeplinit a: Un alt r de execut ie informeaz a pe cei care a steapt a la un anumit monitor s a se trezeasc a - acest lucru se realizeaz a printr-un apel al metodei notifyAll sau notify. Perioada de a stepatare specicat a a expirat. Metoda wait poate produce except ii de tipul InterruptedException, atunci c and rul de execut ie care a steapt a (este deci n starea Not Runnable) este ntrerupt din a steptare si trecut fort at n starea Runnable, de si condit ia a steptat a nu era nc a ndeplinit a. Metoda notifyAll informeaz a toate rele de execut ie care sunt n asteptare la monitorul obiectului curent ndeplinirea condit iei pe care o a steptau. Metoda notify informeaz a doar un singur r de execut ie, specicat ca argument. Reamintim varianta corect a a clasei Buffer: Listing 12.9: Folosirea semafoarelor
class Buffer { private int number = -1;

AL UNUI FIR DE EXECUT 12.3. CICLUL DE VIAT A IE


private boolean available = false ; public synchronized int get () { while (! available ) { try { wait () ; // Asteapta producatorul sa puna o valoare } catch ( InterruptedException e ) { e . printStackTrace () ; } } available = false ; notifyAll () ; return number ; } public synchronized void put ( int number ) { while ( available ) { try { wait () ; // Asteapta consumatorul sa preia valoarea } catch ( InterruptedException e ) { e . printStackTrace () ; } } this . number = number ; available = true ; notifyAll () ; } }

371

12.3.8

Probleme legate de sincronizare

Din p acate, folosirea monitoarelor ridic a si unele probleme. S a analiz am c ateva dintre ele si posibilele lor solut ii: Deadlock Deadlock-ul este o problem a clasic a ntr-un mediu n care ruleaz a mai multe re de execut ie si const a n faptul c a, la un moment dat, ntreg procesul se poate bloca deoarece unele re a steapt a deblocarea unor monitoare care nu se vor debloca niciodat a. Exist a numeroase exemple n acest sens, cea mai cunoscut a ind Problema lozolor. Reformulat a, s a ne imagin am dou a persoane A si B (re de execut ie) care stau la aceea si mas a si tre-

372

CAPITOLUL 12. FIRE DE EXECUT IE

buie s a foloseasc a n comun cut itul si furculit a (resursele comune) pentru a m anca. Evident, cele dou a persoane doresc obt inerea ambelor resurse. S a presupunem c a A a ot inut cut itul si B furculit a. Firul A se va bloca n a steptarea eliber arii furculit ei iar rul A se va bloca n a stepatrea eliber arii cut itului, ceea ce conduce la starea de deadlock. De si acest exemplu este desprins de realitate, exist a numeroase situat ii n care fenomenul de deadlock se poate manifesta, multe dintre acestea ind dicil de detectat. Exist a c ateva reguli ce pot aplicate pentru evitarea deadlock-ului: Firele de execut ie s a solicite resursele n aceea si ordine. Aceast a abordare elimin a situat iile de a steptare circular a. Folosirea unor monitoare care s a controleze accesul la un grup de resurse. In cazul nostru, putem folosi un monitor tac amuri care trebuie blocat nainte de a cere furculit a sau cut itul. Folosirea unor variabile care s a informeze disponibilitatea resurselor f ar a a bloca monitoarele asociate acestora. Cel mai importat, conceperea unei arhitecturi a sistemului care s a evite pe c at posibil aparit ia unor potent iale situat ii de deaslock.

Variabile volatile Cuv antul cheie volatile a fost introdus pentru a controla unele aspecte legate de optimiz arile efectuate de unele compilatoare. S a consider am urm atorul exemplu: class TestVolatile { boolean test; public void metoda() { test = false; // * if (test) { // Aici se poate ajunge... } } }

12.4. GRUPAREA FIRELOR DE EXECUT IE

373

Un compilator care optimizeaz a codul, poate decide c a variabila test ind setat a pe false, corpul if -ului nu se va executa si s a exclud a secvent a respectiv a din rezultatul compil arii. Dac a aceast a clas a ar ns a accesat a de mai multe re de execut ie, variabile test ar putea setat a pe true de un alt r, exact ntre instruct iunile de atribuire si if ale rului curent. Declararea unei variabile cu modicatorul volatile informeaz a compilatorul s a nu optimizeze codul n care aceasta apare, previzion and valoarea pe care variabila o are la un moment dat.

Fire de execut ie inaccesibile Uneori rele de execut ie sunt blocate din alte motive dec at a steptarea la un monitor, cea mai frecvent a situat ie de acest tip ind operat iunile de intrare/ie sire (IO) blocante. C and acest lucru se nt ampl a celelalte re de execut ie trebuie s a poat a accesa n continuare obiectul. Dar dac a operat iunea IO a fost f acut a ntr-o metod a sincronizat a, acest lucru nu mai este posibil, monitorul obiectului ind blocat de rul care a steapt a de fapt s a realizeze operat ia de intrare/ie sire. Din acest motiv, operat iile IO nu trebuie f acute n metode sincronizate.

12.4

Gruparea relor de execut ie

Gruparea relor de execut ie pune la dispozit ie un mecanism pentru manipularea acestora ca un tot si nu individual. De exemplu, putem s a pornim sau s a suspend am toate rele dintr-un grup cu un singur apel de metod a. Gruparea relor de execut ie se realizeaz a prin intermediul clasei ThreadGroup. Fiecare r de execut ie Java este membru al unui grup, indiferent dac a specic am explicit sau nu acest lucru. Alierea unui r la un anumit grup se realizeaz a la crearea sa si devine permanent a, n sensul c a nu vom putea muta un r dintr-un grup n altul, dup a ce acesta a fost creat. In cazul n care cre am un r folosind un constructor care nu specic a din ce grup face parte, el va plasat automat n acela si grup cu rul de execut ie care l-a creat. La pornirea unui program Java se creeaz a automat un obiect de tip ThreadGroup cu numele main, care va reprezenta grupul tuturor relor de execut ie create direct din program si care nu au fost ata sate explicit altui grup. Cu alte cuvinte, putem s a ignor am complet plasarea relor de execut ie n grupuri si s a l as am sistemul s a se ocupe cu aceasta, adun andu-le pe toate

374

CAPITOLUL 12. FIRE DE EXECUT IE

n grupul main. Exist a situat ii ns a c and gruparea relor de execut ie poate u sura substant ial manevrarea lor. Crearea unui r de execut ie si plasarea lui ntr-un grup (altul dec at cel implicit) se realizeaz a prin urm atorii constructori ai clasei Thread: public Thread(ThreadGroup group, Runnable target) public Thread(ThreadGroup group, String name) public Thread(ThreadGroup group, Runnable target, String name) Fiecare din ace sti costructori creeaz a un r de execut ie, l init ializeaz a si l plaseaz a ntr-un grup specicat ca argument. Pentru a aa c arui grup apart ine un anumit r de execut ie putem folosi metoda getThreadGroup a clasei Thread. In exemplul urm ator vor create dou a grupuri, primul cu dou a re de execut ie iar al doilea cu trei: 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 p arinte un alt grup, ceea ce nseamn a c a rele de execut ie pot plasate ntr-o ierarhie de grupuri, n care r ad acina este grupul implicit main, ca n gura de mai jos:

12.4. GRUPAREA FIRELOR DE EXECUT IE S a consider am un exemplu m care list am rele de execut ie active:

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 EXECUT IE

12.5

Comunicarea prin uxuri de tip pipe

O modalitate deosebit de util a prin care dou a re de execut ie pot comunica este realizat a prin intermediul canalelor de comunicatii (pipes). Acestea sunt implementate prin uxuri descrise de clasele: PipedReader, PipedWriter - pentru caractere, respectiv PipedOutputStream, PipedInputStream - pentru octet i. Fluxurile pipe de ie sire si cele de intrare pot conectate pentru a efectua transmiterea datelor. Acest lucru se realizeaz a uzual prin intemediul constructorilor: public PipedReader(PipedWriterpw) public PipedWriter(PipedReaderpr) In cazul n care este folosit un constructor f ar a argumente, conectarea unui ux de intrare cu un ux de ie sire se face prin metoda connect: public void connect(PipedWriterpw) public void connect(PipedReaderpr) Intruc at uxurile care sunt conectate printr-un pipe trebuie s a execute simultan operat ii de scriere/citire, folosirea lor se va face din cadrul unor re de execut ie. Funct ionarea obicetelor care instant iaz a PipedWriter si PipedReader este asem an atoare cu a canalelor de comunicare UNIX (pipes). Fiecare cap at al unui canal este utilizat dintr-un r de execut ie separat. La un cap at se scriu caractere, la cel alalt se citesc. La citire, dac a nu sunt date disponibile rul de execut ie se va bloca p an a ce acestea vor deveni disponibile. Se observ a c a acesta este un comportament tipic produc ator-consumator asincron, rele de execut ie comunic and printr-un canal. 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 si citirea pe/de pe canale se realizeaz a prin metodele uzuale read si write, n toate formele lor. S a reconsider am acum exemplul produc ator/consumator prezentat anterior, folosind canale de comunicat ie. Produc atorul trimite datele printr-un ux de ie sire de tip DataOutputStream c atre consumator, care le prime ste printr-un ux de intrare de tip DataInputStream. Aceste dou a uxuri vor 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 ( InterruptedException e ) { } } } } class Consumator extends Thread { private DataInputStream in ; public Consumator ( DataInputStream in ) { this . in = in ;

378
}

CAPITOLUL 12. FIRE DE EXECUT IE

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 PipedOutputStream () ; 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 si TimerTask

Clasa Timer ofer a o facilitate de a planica diverse act iuni pentru a realizate la un anumit moment de c atre un r de execut ie ce ruleaz a n fundal. Act iunile unui obiect de tip Timer sunt implementate ca instant e ale clasei TimerTask si pot programate pentru o singur a execut ie sau pentru execut ii repetate la intervale regulate. Pa sii care trebuie f acut i pentru folosirea unui timer sunt:

12.6. CLASELE TIMER S I TIMERTASK

379

Crearea unei subclase Actiune a lui TimerTask si supreadenirea metodei run ce va cont ine act iunea pe care vrem s a o planic am. Dup a cum vom vedea, pot folosite si clase anonime. Crearea unui r de execut ie prin instant ierea clasei Timer; Crearea unui obiect de tip Actiune; Planicarea la execut ie a obiectuluii de tip Actiune, folosind metoda schedule din clasa Timer; Metodele de planicare pe care le avem la dispozit ie au urm atoarele formate: 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 act iunea ce se va executa, delay reprezint a nt arzierea fat a de momentul curent dup a care va ncepe execut ia, time momentul exact la care va ncepe execut ia iar period intervalul de timp ntre dou a execut ii. Dup a cum se observ a, metodele de planicare se mpart n dou a categorii: schedule - planicare cu nt arziere x a: dac a dintr-un anumit motiv act iunea este nt arziat a, urm atoarele act iuni vor si ele nt arziate n consecint a; scheduleAtFixedRate - planicare cu num ar x de rate: dac a dintrun anumit motiv act iunea este nt arziat a, urm atoarele act iuni vor executat a mai repede, astfel nc at num arul total de act iuni dintr-o perioad a de timp s a e tot timpul acela si; Un timer se va opri natural la terminarea metodei sale run sau poate oprit fort at folosind metoda cancel. Dup a oprirea sa el nu va mai putea folosit pentru planicarea altor act iuni. De asemenea, metoda System.exit va oprit fort at toate rele de execut ie si va termina aplicat ia curent a.

380

CAPITOLUL 12. FIRE DE EXECUT IE Listing 12.12: Folosirea claselor Timer si TimerTask

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 . scheduleAtFixedRate ( 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 S I TIMERTASK

381

Timer t3 = new Timer () ; t3 . schedule ( new Alarma ( " Toti copiii la culcare ! " ) , ora ) ; } }

382

CAPITOLUL 12. FIRE DE EXECUT IE

Capitolul 13 Programare n ret ea


13.1 Introducere

Programarea n ret ea implic a trimiterea de mesaje si date ntre aplicat ii ce ruleaz a pe calculatoare aate ntr-o ret ea local a sau conectate la Internet. Pachetul care ofer a suport pentru scrierea aplicatiilor de ret ea este java.net. Clasele din acest pachet ofer a o modalitate facil a de programare n ret ea, f ar a a nevoie de cuno stint e prealabile referitoare la comunicarea efectiv a ntre calculatoare. Cu toate acestea, sunt necesare c ateva not iuni fundamentale referitoare la ret ele cum ar : protocol, adresa IP, port, socket.

Ce este un protocol ? Un protocol reprezint a o convent ie de reprezentare a datelor folosit a n comunicarea ntre dou a calculatoare. Av and n vedere faptul c a orice informat ie care trebuie trimis a prin ret ea trebuie serializat a astfel nc at s a poat a transmis a secvent ial, octet cu octet, c atre destinat ie, era nevoie de stabilirea unor convent ii (protocoale) care s a e folosite at at de calculatorul care trimite datele c at si de cel care le prime ste, pentru a se nt elege ntre ele. Dou a dintre cele mai utilizate protocoale sunt TCP si UDP. TCP (Transport Control Protocol) este un protocol ce furnizeaz a un ux sigur de date ntre dou a calculatoare aate n ret ea. Acest protocol asigur a stabilirea unei conexiuni permanente ntre cele dou a calculatoare pe parcursul comunicat iei. UDP (User Datagram Protocol) este un protocol bazat pe pachete inde383

384

CAPITOLUL 13. PROGRAMARE IN RET EA pendente de date, numite datagrame, trimise de la un calculator c atre altul f ar a a se garanta n vreun fel ajungerea acestora la destinat ie sau ordinea n care acestea ajung. Acest protocol nu stabile ste o conexiun a permant a ntre cele dou a calculatoare.

Cum este identicat un calculator n ret ea ? Orice calculator conectat la Internet este identicat n mod unic de adresa sa IP (IP este acronimul de la Internet Protocol). Aceasta reprezint a un num ar reprezentat pe 32 de bit i, uzual sub forma a 4 octet i, cum ar de exemplu: 193.231.30.131 si este numit adresa IP numeric a. Corespunz atoare unei adrese numerice exista si o adresa IP simbolic a, cum ar thor.infoiasi.ro pentru adresa numeric a anterioar a. De asemenea, ecare calculator aat ntr-o ret ea local a are un nume unic ce poat folosit la identicarea local a a acestuia. Clasa Java care reprezint a not iunea de adres a IP este InetAddress.

Ce este un port ? Un calculator are n general o singur a leg atur a zic a la ret ea. Orice informat ie destinat a unei anumite ma sini trebuie deci s a specice obligatoriu adresa IP a acelei ma sini. Ins a pe un calculator pot exista concurent mai multe procese care au stabilite conexiuni n ret ea, astept and diverse informat ii. Prin urmare, datele trimise c atre o destinat ie trebuie s a specice pe l anga adresa IP a calculatorului si procesul c atre care se ndreapt a informat iile respective. Identicarea proceselor se realizeaz a prin intermdiul porturilor. Un port este un num ar pe 16 bit i care identic a n mod unic procesele care ruleaz a pe o anumit a masin a. Orice aplicat ie care realizeaz a o conexiune n ret ea va trebui s a ata seze un num ar de port acelei conexiuni. Valorile pe care le poate lua un num ar de port sunt cuprinse ntre 0 si 65535 (deoarece sunt numere reprezentate pe 16 bit i), numerele cuprinse ntre 0 si 1023 ind ns a rezervate unor servicii sistem si, din acest motiv, nu trebuie folosite n aplicat ii.

Clase de baz a din java.net Clasele din java.net permit comunicarea ntre procese folosind protocoalele

13.2. LUCRUL CU URL-URI TCP si UDP si sunt prezentate n tabelul de mai jos. TCP URL URLConnection Socket ServerSocket UDP DatagramPacket DatagramSocket MulticastSocket

385

13.2

Lucrul cu URL-uri

Termenul URL este acronimul pentru Uniform Resource Locator si reprezint a o referint a (adres a) la o resurs a aat a pe Internet. Aceasta este n general un sier reprezent and o pagin a Web, un text, imagine, etc., ns a un URL poate referi si interog ari la baze de date, rezultate ale unor comenzi executate la distant a, etc. Mai jost, sunt prezentate c ateva exemple de URL-uri sunt: 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 a cum se observ a din exemplele de mai sus, un URL are dou a componente principale: Identicatorul protocolului folosit (http, ftp, etc); Numele resursei referite. Acesta are urm atoarele componente: Numele calculatorului gazd a (www.infoiasi.ro). Calea complet a spre resursa referit a ( acf/java/curs/9/prog retea.html). Notat ia user semnic a uzual subdirectorul html al directorului rezervat pe un server Web utilizatorului specicat (HOME). In cazul n care este specicat doar un director, sierul ce reprezint a resursa va considerat implicit index.html. Opt ional, o referint a de tip anchor n cadrul sierului referit (#url). Opt ional, portul la care s a se realizeze conexiunea.

386

CAPITOLUL 13. PROGRAMARE IN RET EA

Clasa care permite lucrul cu URL-uri este java.net.URL. Aceasta are mai mult i constructori pentru crearea de obiecte ce reprezint a referint e c atre resurse aate n ret ea, cel mai uzual ind cel care prime ste ca parametru un sir de caractere. In cazul n care sirul nu reprezint a un URL valid va aruncat a o except ie de tipul MalformedURLException. 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 informat iilor despre resursa referit a (numele calculatorului gazd a, numele sierului, protocolul folosit. etc). Citirea printr-un ux a continutului sierului respectiv. Conectarea la acel URL pentru citirea si scrierea de informat ii.

Citirea cont inutului unui URL Orice obiect de tip URL poate returna un ux de intrare de tip InputStream pentru citirea continutului s au. Secvent a standard pentru aceast a operatiune este prezentat a n exemplul de mai jos, n care a sam cont inutul resursei specicat a la linia de comand a. Dac a nu se specic a mici un argument, va a sat sierul index.html de la adresa: http://www.infoiasi.ro. Listing 13.1: Citirea cont inutului 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 InputStreamReader ( in ) ) ; String linie ; while (( linie = br . readLine () ) != null ) { // Afisam linia citita System . out . println ( linie ) ; } } catch ( Malfo rmedU RLEx cepti on e ) { System . err . println ( " URL invalid !\ n " + e ) ; } finally { br . close () ; } } }

387

Conectarea la un URL Se realizeaz a prin metoda openConnection ce stabile ste o conexiune bidirect ional a cu resursa specicat a. Aceast a conexiune este reprezentat a de un obiect de tip URLConnection, ce permite crearea at at a unui ux de intrare pentru citirea informat iilor de la URL-ul specicat, c at si a unui ux de ie sire pentru scrierea de date c atre acel URL. Operat iunea de trimitere de date dintr-un program c atre un URL este similar a cu trimiterea de date dintr-un formular de tip FORM aat ntr-o pagin a HTML. Metoda folosit a pentru trimitere este 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

Denit ie Un socket (soclu) este o abstract iune software folosit a pentru a reprezenta ecare din cele dou a capete ale unei conexiuni ntre dou a procese ce ruleaz a ntr-o ret ea. Fiecare socket este ata sat unui port astfel nc at s a poat a identica unic programul c aruia i sunt destinate datele.

388

CAPITOLUL 13. PROGRAMARE IN RET EA

Socket-urile sunt de dou a tipuri: TCP, implementate de clasele Socket si ServerSocket; UDP, implementate de clasa DatagramSocket. O aplicat ie de ret ea ce folose ste socket-uri se ncadreaz a n modelul client/server de concepere a unei aplicat ii. In acest model aplicat ia este format a din dou a categorii distincte de programe numite servere, respectiv client i. Programele de tip server sunt cele care ofer a diverse servicii eventualilor client i, ind n stare de a steptare at ata vreme c at nici un client nu le solicit a serviciile. Programele de tip client sunt cele care init iaz a conversat ia cu un server, solicit and un anumit serviciu. Uzual, un server trebuie s a e capabil s a trateze mai mult i clienti simultan si, din acest motiv, ecare cerere adresat a serverului va tratat a ntr-un r de execut ie separat.

Incep and cu versiunea 1.4 a platformei standard Java, exist a o clas a utilitar a care implementeaz a o pereche de tipul (adresa IP, num ar port). Aceasta este InetSocketAddress (derivat a din SocketAddress), obiectele sale ind utilizate de constructori si metode denite n cadrul claselor ce descriu socketuri, pentru a specica cei doi parametri necesari identic arii unui proces care trimite sau recept ioneaz a date n ret ea.

13.4

Comunicarea prin conexiuni

In acest model se stabile ste o conexiune TCP ntre o aplicat ie client si o aplicat ie server care furnizeaz a un anumit serviciu. Avantajul protocolul TCP/IP este c a asigur a realizarea unei comunic ari stabile, permanente n ret ea, exist and sigurant a c a informat iile trimise de un proces vor recept ionate corect si complet la destinat ie sau va semnalat a o except ie n caz contrar. Leg atura ntre un client si un server se realizeaz a prin intermediul a dou a obiecte de tip Socket, c ate unul pentru ecare cap at al canalului de comunicat ie dintre cei doi. La nivelul clientului crearea socketului se realizeaz a specic and adresa IP a serverului si portul la care ruleaz a acesta, constructorul uzual folosit ind: Socket(InetAddress address, int port)

13.4. COMUNICAREA PRIN CONEXIUNI

389

La nivelul serverului, acesta trebuie s a creeze nt ai un obiect de tip ServerSocket. Acest tip de socket nu asigur a comunicarea efectiv a cu client ii ci este responsabil cu ascultarea ret elei si crearea unor obiecte de tip Socket pentru ecare cerere ap arut a, prin intermediul c aruia va realizat a leg atura cu clientul. Crearea unui obiect de tip ServerSocket se face specic and portul la care ruleaz a serverul, constructorul folosit ind: ServerSocket(int port) Metoda clasei ServerSocket care a steapt a ascult a ret eaua este accept. Aceasta blocheaz a procesul p arinte p an a la aparit ia unui cereri si returneaz a un nou obiect de tip Socket ce va asigura comunicarea cu clientul. Blocarea poate s a nu e permanent a ci doar pentru o anumit a perioad a de timp aceasta va specicat a prin metoda setSoTimeout, cu argumentul dat n milisecunde.

Pentru ecare din cele dou a socketuri deschise pot create apoi dou a uxuri pe octet i pentru citirea, respectiv scrierea datelor. Acest lucru se realizeaz a prin intermediul metodelor getInputStream, respectuv getOutputStream. Fluxurile obt inute vor folosite mpreun a cu uxuri de procesare care s a asigure o comunicare facil a ntre cele dou a procese. In funct ie de specicul aplicat iei acestea pot perechile: BufferedReader, BufferedWriter si PrintWriter - pentru comunicare prin intermediul sirurilor de caractere; DataInputStream, DataOutputStream - pentru comunicare prin date primitive; ObjectInputStream, ObjectOutputStream - pentru cominicare prin intermediul obiectelor;

390

CAPITOLUL 13. PROGRAMARE IN RET EA

Structura general a a unui server bazat pe conexiuni este: 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 a se realizeze n re de execut ie separate, pentru ca metoda accept s a poat a reapelat a c at mai repede n vederea stabilirii conexiunii cu un alt client. Structura general a a unui client bazat pe conexiuni este:

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 urm ator vom implementa o aplicat ie client-server folosind comunicarea prin conexiuni. Clientul va trimite serverului un nume iar acesta va raspunde prin mesajul Hello nume. Tratarea cererilor se va face n re de execut ie 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 IN RET EA


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 InputStreamReader ( 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 ( UnknownHostException 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 a o conexiune permanent a ntre client si server prin intermediul c areia s a se realizeze comunicarea. Clientul trimite cererea c atre server prin intermediul unuia sau mai multor pachete de date independente, serverul le recept ioneaz a, extrage informat iile cont inute si returneaz a r aspunsul tot prin intermediul pachetelor. Un astfel de pachet se nume ste datagram a si este reprezentat printr-un obiect din clasa DatagramPacket. Rutarea datagramelor de la o ma sin a la alta se face exclusiv pe baza informat iilor cont inute de acestea. Primirea si trimiterea datagramelor se realizeaz a prin intermediul unui socket, modelat prin intermediul clasei DatagramSocket.

394

CAPITOLUL 13. PROGRAMARE IN RET EA

Dup a cum am ment ionat deja, dezavantajul acestei metode este c a nu garanteaz a ajungerea la destinat ie a pachetelor trimise si nici c a vor primite n aceea si ordinie n care au fost expediate. Pe de alt a parte, exist a situat ii n care aceste lucruri nu sunt importante si acest model este de preferat celui bazat pe conexiuni care solicit a mult mai mult at at serverul c at si clientul. De fapt, protocolul TCP/IP folose ste tot pachete pentru trimiterea informat iilor dintr-un nod n altul al ret elei, cu deosebirea c a asigur a respectarea ordinii de transmitere a mesajelor si veric a ajungerea la destinat ie a tuturor pachetelor - n cazul n care unul nu a ajuns, acesta va retrimis automat. Clasa DatagramPacket cont ine urm atorii constructori: 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 a perechi de constructori sunt pentru creare pachetelor ce vor expediate, diferent a ntre ele ind utilizarea claselor InetAddress, respectiv SocketAddress pentru specicarea adresei desinat ie. A trei pereche de constructori este folosit a pentru crearea unui pachet n care vor recept ionate date, ei nespecic and vreo surs a sau destinat ie.

13.5. COMUNICAREA PRIN DATAGRAME

395

Dup a crearea unui pachet procesul de trimitere si primire a acestuia implic a apelul metodelor send si receive ale clasei DatagramSocket. Deoarece toate informat ii sunt incluse n datagram a, acela si socket poate folosit at at pentru trimiterea de pachete, eventual c atre destinat ii diferite, c at si pentru recept ionarea acestora de la diverse surse. In cazul n care refolosim pachete, putem schimba cont inutul acestora cu metoda setData, precum si adresa la care le trimitem prin setAddress, setPort si setSocketAddress. Extragerea informat iilor cont iunte de un pachet se realizeaz a prin metoda getData din clasa DatagramPacket. De asemenea, aceast a clas a ofer a metode pentru aarea adresei IP si a portului procesului care a trimis datagrama, pentru a-i putea r aspunde dac a este necesar. Acestea sunt: getAdress, getPort si getSocketAddress. 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 IN RET EA


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 MULT I CLIENT I


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 c atre mai mult i client i

Diverse situat ii impun gruparea mai multor client i astfel nc at un mesaj (pachet) trimis pe adresa grupului s a e recept ionat de ecare dintre ace stia. Gruparea mai multor programe n vederea trimiterii multiple de mesaje se realizeaz a prin intermediul unui socket special, descris de clasa MulticastSocket, extensie a clasei DatagramSocket. Un grup de client i abonat i pentru trimitere multipl a este specicat printro adres a IP din intervalul 224.0.0.1 - 239.255.255.255 si un port UDP. Adresa 224.0.0.0 este rezervat a si nu trebuie folosit a.

398

CAPITOLUL 13. PROGRAMARE IN RET EA

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 MULT I CLIENT I

399

System . out . println ( new String ( packet . getData () ) . trim () ) ; } finally { if ( socket != null ) { socket . leaveGroup ( group ) ; socket . close () ; } } } }

Listing 13.7: Transmiterea unui mesaj c atre un grup


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 IN RET EA

Capitolul 14 Appleturi
14.1 Introducere

Denit ie Un applet reprezint a un program Java de dimensiuni reduse ce gestioneaz a o suprafat a de a sare (container) care poate inclus a ntr-o pagin a Web. Un astfel de program se mai nume ste miniaplicatie. Ca orice alt a aplicat ie Java, codul unui applet poate format din una sau mai multe clase. Una dintre acestea este principal a si extinde clasa Applet, aceasta ind clasa ce trebuie specicat a n documentul HTML ce descrie pagina Web n care dorim s a includem appletul. Diferent a fundamental a dintre un applet si o aplicat ie const a n faptul c a un applet nu poate executat independent, ci va executat de browserul n care este nc arcat a pagina Web ce cont ine appletul respectiv. O aplicat ie independent a este executat a prin apelul interpretorului java, av and ca argument numele clasei principale a aplicat iei, clasa principal a ind cea care cont ine metoda main. Ciclul de viat a al unui applet este complet diferit, ind dictat de evenimentele generate de c atre browser la vizualizarea documentului HTML ce cont ine appletul. Pachetul care ofer a suport pentru crearea de appleturi este java.applet, cea mai important a clas a ind Applet. In pachetul javax.swing exist a si clasa JApplet, care extinde Applet, oferind suport pentru crearea de appleturi pe arhitectura de componente JFC/Swing. 401

402

CAPITOLUL 14. APPLETURI

Ierarhia claselor din care deriv a appleturile este prezentata n gura de mai jos:

Fiind derivat a din clasa Container, clasa Applet descrie de fapt suprafet e de a sare, asemenea claselor Frame sau Panel.

14.2

Crearea unui applet simplu

Crearea structurii de siere si compilarea applet-urilor sunt identice ca n cazul aplicat iilor. Difer a n schimb structura programului si modul de rulare a acestuia. S a parguream n continuare ace sti pa si pentru a realiza un applet extrem de simplu, care a seaz a o imagine si un sir de caractere.

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 a de browser, clasa principal a a appletului trebuie s a e public a.

2. Salvarea sierelor surs a Ca orice clas a public a, clasa principala a appletului va salvat a ntr-un sier cu acela si nume si extensia .java. A sadar, vom salva clasa de mai sus ntr-un sier FirstApplet.java.

3. Compilarea Compilarea se face la fel ca si la aplicat iile independente, folosind compilatorul javac apelat pentru sierul ce cont ine appletul. javac FirstApplet.java In cazul n care compilarea a reu sit va generat sierul FirstApplet.class.

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 a facem dou a operat ii: Crearea unui sier HTML n care vom include applet-ul. S a consider am sierul simplu.html, av and cont inutul de mai jos:

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 efectu and apelul: appletviewer simplu.html.

14.3

Ciclul de viat a al unui applet

Execut ia unui applet ncepe n momentul n care un browser a seaz a o pagin a Web n care este inclus appletul respectiv si poate trece prin mai multe etape. Fiecare etap a este str ans legat a de un eveniment generat de c atre browser si determin a apelarea unei metode specice din clasa ce implementeaz a appletul. Inc arcarea n memorie Este creat a o instant a a clasei principale a appletului si ncarcat a n memorie. Init ializarea Este apelat a metoda init ce permite init ializarea diverselor variabile, citirea unor parametri de intrare, etc. Pornirea Este apelat a metoda start Execut ia propriu-zis a Const a n interact iunea dintre utilizator si componentele a sate pe suprafat a appletului sau n executarea unui anumit cod ntr-un r de execut ie. In unele situat ii ntreaga execut ie a appletului se consum a la etapele de init ializare si pornire.

AL UNUI APPLET 14.3. CICLUL DE VIAT A

405

Oprirea temporar a In cazul n care utilizatorul p ar ase ste pagina Web n care ruleaz a appletul este apelat a metoda stop a acestuia, d andu-i astfel posibilitatea s a opreasca temporar execut ia sa pe perioada n care nu este vizibil, pentru a nu consuma inutil din timpul procesorului. Acela si lucru se nt ampl a dac a fereastra browserului este minimizat a. In momentul c and pagina Web ce contine appletul devine din nou activ a, va reapelat a metoda start. Oprirea denitiv a La nchiderea tuturor instant elor browserului folosit pentru vizualizare, appletul va eliminat din memorie si va apelat a metoda destroy a acestuia, pentru a-i permite s a elibereze resursele det inute. Apelul metodei destroy este ntotdeauna precedat de apelul lui stop.

Metodele specice appleturilor A sadar, exist a o serie de metode specice appleturilor ce sunt apelate automat la diverse evenimente generate de c atre browser. Acestea sunt denite n clasa Applet si sunt enumerate n tabelul de mai jos: Metoda Situat ia n care este apelat a init La init ializarea appletului. Teoretic, aceast a metod a ar trebui s a se apeleze o singur a dat a, la prima a sare a appletului n pagin a, ns a, la unele browsere, este posibil ca ea s a se apeleze de mai multe ori. start Imediat dup a init ializare si de ecare dat a c and appletul redevine activ, dup a o oprire temporar a. stop De ecare dat a c and appletul nu mai este vizibil (pagina Web nu mai este vizibil a, fereastra browserului este minimizat a, etc) si nainte de destroy. destroy La nchiderea ultimei instant e a browserului care a nc arcat n memorie clasa principal a a appletului.

Atent ie

406

CAPITOLUL 14. APPLETURI

Aceste metode sunt apelate automat de browser si nu trebuie apelate explicit din program !

Structura general a a unui applet 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

Interfat a grac a cu utilizatorul

Dup a cum am v azut, clasa Applet este o extensie a superclasei Container, ceea ce nseamn a c a appleturile sunt, nainte de toate, suprafet e de a sare. Plasarea componentelor, gestionarea pozit ion arii lor si tratarea evenimentelor generate se realizeaz a la fel ca si n cazul aplicat iilor. Uzual, ad augarea componentelor pe suprafat a appletului precum si stabilirea obiectelor responsabile cu tratarea evenimentelor generate sunt operat iuni ce vor realizate n metoda init.

CU UTILIZATORUL 14.4. INTERFAT A GRAFICA

407

Gestionarul de pozit ionare implicit este FlowLayout, ns a acesta poate schimbat prin metoda setLayout.

408

CAPITOLUL 14. APPLETURI

Desenarea pe suprafat a unui applet Exist a o categorie ntreag a de appleturi ce nu comunic a cu utilizatorul prin intermediul componentelor ci, execut ia lor se rezum a la diverse operat iuni de desenare realizate n metoda paint. Reamintim c a metoda paint este responsabil a cu denirea aspectului grac al oric arei componente. Implicit, metoda paint din clasa Applet nu realizeaz a nimic, deci, n cazul n care dorim s a desen am direct pe suprafat a unui applet va nevoie s a supradenim aceast a metod a. public void paint(Graphics g) { // Desenare ... } In cazul n care este aleas a aceast a solut ie, evenimentele tratate uzual vor cele generate de mouse sau tastatur a.

14.5

Denirea si folosirea parametrilor

Parametrii sunt pentru appleturi ceea ce argumentele de la linia de comand a sunt pentru aplicat iile independente. Ei permit utilizatorului s a personalizeze aspectul sau comportarea unui applet f ar a a-i schimba codul si recompila clasele. Denirea parametrilor se face n cadrul tagului APPLET din documentul HTML ce cont ine appletul si sunt identicat i prin atributul PARAM. Fiecare parametru are un nume, specicat prin NAME si o valoare, specicat a prin VALUE, ca n exemplul de mai jos: <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 si n cazul argumentelor trimise aplicat iilor de la linia de comand a, tipul parametrilor este ntotdeauna sir de caractere, indiferent dac a valoarea este ntre ghilimele sau nu. Fiecare applet are si un set de parametri prestabilit i ale c aror nume nu vor putea folosite pentru denirea de noi parametri folosind metoda de

14.5. DEFINIREA S I FOLOSIREA PARAMETRILOR

409

mai sus. Ace stia apar direct n corpul tagului APPLET si denesc informat ii generale despre applet. Exemple de astfel de parametri sunt CODE, WIDTH sau HEIGHT. Lista lor complet a va prezentata la descrierea tagului APPLET. Folosirea parametrilor primit i de c atre un applet se face prin intermediul metodei getParameter care prime ste ca argument numele unui parametru si returneaz a valoarea acestuia. In cazul n care nu exist a nici un parametru cu numele specicat, metoda ntoarce null, caz n care programul trebuie s a atribuie o valoare implicit a variabilei n care se dorea citirea respectivului parametru. Orice applet poate pune la dispozit ie o documentat ie referitoare la parametrii pe care i suport a, pentru a veni n ajutorul utilizatorilor care doresc s a includ a appletul ntr-o pagin a Web. Aceasta se realizeaz a prin supradenirea metodei getParameterInfo, care returneaz a un vector format din triplete de siruri. Fiecare element al vectorului este de fapt un vector cu trei elemente de tip String, cele trei siruri reprezent and numele parametrului, tipul s au si o descriere a sa. Informat iile furnizate de un applet pot citite din browserul folosit pentru vizualizare prin metode specice acestuia. De exemplu, n appletviewer informat iile despre parametri pot vizualizate la rubrica Info din meniul Applet, n Netscape se folose ste opt iunea Page info din meniul View, etc. S a scriem un applet care s a a seze un text primit ca parametru, folosind un font cu numele si dimensiunea specicate de asemenea ca parametri. 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 erFor matEx cepti on 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 a tagului APPLET, cu ajutorul c aruia pot incluse appleturi n cadrul paginilor Web este: <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 p atrate sunt opt ionale.

411

CODE = clasaApplet Numele sierului ce cont ine clasa principal a a appletului. Acesta va c autat n directorul specicat de CODEBASE. Nu poate absolut si trebuie obligatoriu specicat. Extensia .class poate sau nu s a apar a. WIDTH =latimeInPixeli, HEIGHT =inaltimeInPixeli Specic a l a timea si n alt imea suprafet ei n care va a sat appletul. Sunt obligatorii. ARCHIVE = arhiva.jar Specic a arhiva n care se g asesc clasele appletului. CODEBASE = directorApplet Specic a URL-ul la care se g ase ste clasa appletului. Uzual se exprim a relativ la directorul documentului HTML. In cazul n care lipse ste, se consider a implicit URL-ul documentului. ALT = textAlternativ Specic a textul ce trebuie a sat dac a browserul nt elege tagul APPLET dar nu poate rula appleturi Java. NAME =numeInstantaApplet Ofer a posibilitatea de a da un nume respectivei instant e a appletului, astfel nc at mai multe appleturi aate pe aceea si pagin a s a poat a comunica ntre ele folosindu-se de numele lor. ALIGN =aliniere Semnic a modalitatea de aliniere a appletului n pagina Web. Acest atribut poate primi una din urm atoarele valori: left, right, top,

412

CAPITOLUL 14. APPLETURI

texttop, middle, absmiddle, baseline, bottom, absbottom , seminic lor ind acelea si ca si la tagul IMG. VSPACE =spatiuVertical, HSPACE = spatiuOrizontal Specic a numarul de pixeli dintre applet si marginile suprafetei de a sare. PARAM Tag-urile PARAM sunt folosite pentru specicarea parametrilor unui applet (vezi Folosirea parametrilor). text HTML alternativ Este textul ce va a sat n cazul n care browserul nu ntelege tagul APPLET. Browserele Java-enabled vor ignora acest text.

14.7

Folosirea relor de execut ie n appleturi

La nc arcarea unei pagini Web, ec arui applet i este creat automat un r de execut ie responsabil cu apelarea metodelor acestuia. Acestea vor rula concurent dup a regulile de planicare implementate de ma sina virtual a Java a platformei folosite. Din punctul de vedere al interfet ei grace ns a, ecare applet aat pe o pagin a Web are acces la un acela si r de execut ie, creat de asemenea automat de c atre browser, si care este responsabil cu desenarea appletului (apelul metodelor update si paint) precum si cu transmiterea mesajelor generate de c atre componente. Intruc at toate appleturile de pe pagin a mpart acest r de execut ie, nici unul nu trebuie s a l solicite n mod excesiv, deoarece va provoca funct ionarea anormal a sau chiar blocarea celorlalte. In cazul n care dorim s a efectu am operat iuni consumatoare de timp este recomandat s a le realiz am ntr-un alt r de execut ie, pentru a nu bloca interact iunea utilizatorului cu appletul, redesenarea acestuia sau activitatea celorlalte appleturi de pe pagin a. S a considerm am mai nt ai dou a abord ari gre site de lucru cu appleturi. Dorim s a cre am un applet care s a a seze la coordonate aleatoare mesajul Hello, cu pauz a de o secund a ntre dou a a s ari. Prima variant a, gre sit a de altfel, ar :

14.7. FOLOSIREA FIRELOR DE EXECUT IE IN APPLETURI 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 ( InterruptedException e ) {} } } }

413

Motivul pentru care acest applet nu funct ioneaz a corect si probabil va duce la anomalii n funct ionarea browserului este c a rul de execut ie care se ocup a cu desenarea va r am ane blocat n metoda paint, ncerc and s a o termine. Ca regul a general a, codul metodei paint trebuie s a e c at mai simplu de executat ceea ce, evident, nu este cazul n appletul de mai sus. O alt a idee de rezolvare care ne-ar putea veni, de asemenea gre sit a, este urm atoarea : Listing 14.3: Incorect: appletul nu termin a init ializarea
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 ( InterruptedException e ) {} } } public void paint ( Graphics g ) {

414
g . drawString ( " Hello " , x , y ) ; } }

CAPITOLUL 14. APPLETURI

Pentru a putea da o solut ie corect a problemei propuse, trebuie s a folosim un r de execut ie propriu. Structura unui applet care doreste s a lanseze un r de execut ie poate avea dou a forme. In prima situat ie appletul porne ste rul la init ialzarea sa iar acesta va rula, indiferent dac a appletul mai este sau nu vizibil, p an a la oprirea sa natural a (terminarea metodei run) sau p an a la nchiderea sesiunii de lucru a browserului. Listing 14.4: Corect: folosirea unui r de execut ie 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 ( InterruptedException e ) {} } } public void paint ( Graphics g ) { g . drawString ( " Hello " , x , y ) ; } }

In cazul n care rul de execut ie pornit de applet efectueaz a operatii ce

14.7. FOLOSIREA FIRELOR DE EXECUT IE IN APPLETURI

415

au sens doar dac a appletul este vizibil, cum ar animatie, ar de dorit ca acesta s a se opreasca atunci c and appletul nu mai este vizibil (la apelul metodei stop) si s a reporneasca atunci c and appletul redevine vizibil (la apelul metodei start). Un applet este considerat activ imediat dup a apelul metodei start si devine inactiv la apelul metodei stop. Pentru a aa dac a un applet este activ se folose ste metoda isActive. S a modic am programul anterior, ad aug and si un contor care s a numere a s arile de mesaje - acesta nu va incrementat pe perioada n care appletul nu este activ. Listing 14.5: Folosirea metodelor start si stop
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 ( InterruptedException e ) {}

416
} }

CAPITOLUL 14. APPLETURI

public void paint ( Graphics g ) { g . drawString ( " Hello " + n , x , y ) ; } }

Atent ie Este posibil ca unele browsere s a nu apele metoda stop n situat iile prev azute n specciat iile appleturilor. Din acest motiv, corectitudinea unui applet nu trebuie s a se bazeze pa acest mecanism.

14.8

Alte metode oferite de clasa Applet

Pe l ang a metodele de baz a: init, start, stop, destroy, clasa Applet ofer a metode specice applet-urilor cum ar :

Punerea la dispozitie a unor informat ii despre applet Similar a cu metoda getParameterInfo ce oferea o documentat ie despre parametrii pe care i accept a un applet, exist a metoda getAppletInfo ce permite specicarea unor informat ii legate de applet cum ar numele, autorul, versiunea, etc. Metoda returneaz a un sir de caractere contin and informat iile respective. public String getAppletInfo() { return "Applet simplist, autor necunoscut, ver 1.0"; }

Aarea adreselor URL referitoare la applet Se realizeaz a cu metodele:

14.8. ALTE METODE OFERITE DE CLASA APPLET

417

getCodeBase - ce returneaz a URL-ul directorului ce cont ine clasa appletului; getDocumentBase - returneaz a URL-ul directorului ce cont ine documentul HTML n care este inclus appletul respectiv. Aceste metode sunt foarte utile deoarece permit specicarea relativ a a unor siere folosite de un applet, cum ar imagini sau sunete.

A sarea unor mesaje n bara de stare a browserului Acest lucru se realizeaz a cu metoda showStatus public void init() { showStatus("Initializare applet..."); }

A sarea imaginilor A sarea imaginilor ntr-un applet se face e prin intermediul unei componente ce permite acest lucru, cum ar o suprafat a de desenare de tip Canvas, e direct n metoda paint a applet-ului, folosind metoda drawImage a clasei Graphics. In ambele cazuri, obt inerea unei referint e la imaginea respectiv a se va face cu ajutorul metodei getImage din clasa Applet. Aceasta poate primi ca argument e adresa URL absolut a a sierului ce reprezint a imaginea, e calea relativ a la o anumit a adres a URL, cum ar cea a directorului n care se g ase ste documentul HTML ce cont ine appletul (getDocumentBase) sau a directorului n care se g ase ste clasa appletului (getCodeBase). Listing 14.6: A sarea imaginilor
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 execut ie Contextul de execut ie al unui applet se refer a la pagina n care acesta ruleaz a, eventual mpreun a cu alte appleturi, si este descris de interfat a AppletContext. Crearea unui obiect ce implementeaz a aceast a interfat a se realizeaz a de c atre browser, la apelul metodei getAppletContext a clasei Applet. Prin intermediul acestei interfet e un applet poate vedea n jurul sau, put and comunica cu alte applet-uri aate pe aceeasi pagin a sau cere browser-ului s a deschid a diverse documente. AppletContext contex = getAppletContext();

A sarea unor documente n browser Se face cu metoda showDocument ce prime ste adresa URL a sierului ce cont ine documentul pe care dorim sa-l deschidem (text, html, imagine, etc). Aceast a metod a este accesat a prin intermediul contextului de execut ie al 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 a comunicare implic a de fapt identicarea unui applet aat pe aceea si pagina si apelarea unei metode sau setarea unei variabile publice a acestuia. Identicarea se face prin intermediu numelui pe care orice instant a a unui applet l poate specica prin atributul NAME.

14.8. ALTE METODE OFERITE DE CLASA APPLET

419

Obt inerea unei referint e la un applet al c arui nume l cunoa stem sau obt inerea unei enumer ari a tuturor applet-urilor din pagin a se fac prin intermediul contextului de execut ie, folosind metodele getApplet, respectiv getApplets.

Redarea sunetelor Clasa Applet ofer a si posibilitatea red arii de sunete n format .au. Acestea sunt descrise prin intermediul unor obiecte ce implementeaz a interfat a AudioClip din pachetul java.applet. Pentru a reda un sunet aat ntr-un sier .au la un anumit URL exist a dou a posibilit a ti: Folosirea metodei play din clasa Applet care prime ste ca argument URL-ul la care se a a sunetul; acesta poate specicat absolut sau relativ la URL-ul appletului Crearea unui obiect de tip AudioClip cu metoda getAudioClip apoi apelarea metodelor start, loop si stop pentru acesta.

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 n care appletul folose ste mai multe tipuri de sunete, este recomandat ca nc arcarea acestora s a e f acut a ntr-un r de execut ie separat, pentru a nu bloca temporar activitatea reasc a a programului.

14.9

Arhivarea appleturilor

Dup a cum am v azut, pentru ca un applet aat pe o pagin a Web s a poat a executat codul s au va transferat de pe serverul care g azduie ste pagina Web solicitat a pe ma sina clientului. Deoarece transferul datelor prin ret ea este un proces lent, cu c at dimensiunea sierlor care formeaz a appletul este mai redus a, cu at a nc arcarea acestuia se va face mai repede. Mai mult, dac a appletul cont ine si alte clase n afar a de cea principal a sau diverse resurse (imagini, sunete, etc), acestea vor transferate prin ret ea abia n momentul n care va nevoie de ele, oprind temporar activitatea appletului p an a la nc arcarea lor. Din aceste motive, cea mai ecient a modalitate de a distribui un applet este s a arhiv am toate sierele necesare acestuia. Arhivarea sierelor unui applet se face cu utilitarul jar, oferit n distribut ia J2SDK. // Exemplu jar cvf arhiva.jar ClasaPrincipala.class AltaClasa.class imagine.jpg sunet.au // sau jar cvf arhiva.jar *.class *.jpg *.au

14.10. RESTRICT II DE SECURITATE

421

Includerea unui applet arhivat ntr-o pagin a Web se realizeaz a specic and pe l ang a numele clasei principale si numele arhivei care o cont ine: <applet archive=arhiva.jar code=ClasaPrincipala width=400 height=200 />

14.10

Restrict ii de securitate

Deoarece un applet se execut a pe ma sina utilizatorului care a solicitat pagina Web ce cont ine appletul respectiv, este foarte important s a existe anumite restrict ii de securitate care s a controleze activitatea acestuia, pentru a preveni act iuni r au intent ionate, cum ar stergeri de siere, etc., care s a aduc a prejudicii utilizatorului. Pentru a realiza acest lucru, procesul care ruleaz a appleturi instaleaz a un manager de securitate, adic a un obiect de tip SecurityManager care va superviza activitatea metodelor appletului, arunc and except ii de tip Security Exception n cazul n care una din acestea ncearc a s a efectueze o operat ie nepermis a. Un applet nu poate s a: Citeasc a sau s a scrie siere pe calculatorul pe care a fost ncarcat (client). Deschid a conexiuni cu alte ma sini n afar a de cea de pe care provine (host). Porneasc a programe pe ma sina client. Citeasc a diverse propriet a ti ale sistemului de operare al clientului. Ferestrele folosite de un applet, altele dec at cea a browserului, vor ar ata altfel dec at ntr-o aplicat ie obi snuit a, indic and faptul c a au fost create de un applet.

14.11

Appleturi care sunt si aplicat ii

Deoarece clasa Applet este derivat a din Container, deci si din Component, ea descrie o suprafat a de a sare care poate inclus a ca orice alt a component a ntr-un alt container, cum ar o fereastr a. Un applet poate funct iona si ca o aplicat ie independent a astfel:

422

CAPITOLUL 14. APPLETURI

Ad aug am metoda main clasei care descrie appletul, n care vom face operat iunile urm atoare. Cre am o instant a a appletului si o ad aug am pe suprafat a unei ferestre. Apel am metodele init si start, care ar fost apelate automat de c atre browser. Facem fereastra vizibil a.

Listing 14.8: Applet si aplicat ie


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
Generalit a ti despre baze de date

Aplicat iile care folosesc baze de date sunt, n general, aplicat ii complexe folosite pentru gestionarea unor informat ii de dimensiuni mari ntr-o manier a sigur a si ecient a.

Ce este o baz a de date ? La nivelul cel mai general, o baz a de date reprezint a o modalitate de stocare a unor informat ii (date) pe un suport extern, cu posibilitatea reg asirii acestora. Uzual, o baz a de date este memorat a ntr-unul sau mai multe siere. Modelul clasic de baze de date este cel relat ional, n care datele sunt memorate n tabele. Un tabel reprezint a o structur a de date format a dintr-o mult ime de articole, ecare articol av and denite o serie de atribute - aceste atribute corespund coloanelor tabelului, n timp ce o linie va reprezenta un articol. Pe l anga tabele, o baz a de date mai poate cont ine: proceduri si funct ii, utilizatori si grupuri de utilizatori, tipuri de date, obiecte, etc. Dintre produc atorii cei mai important i de baze de date amintim companiile Oracle, Sybase, IBM, Informix, Microsoft, etc. ecare furniz and o serie ntreag a de produse si utilitare pentru lucrul cu baze de date. Aceste produse sunt n general referite prin termenii DBMS (Database Management System) sau, n traducere, SGBD (Sistem de Gestiune a Bazelor de Date). In acest capitol vom analiza lucrul cu baze de date din perspectiva program arii 423

424

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

n limbajul Java, f ar a a descrie particularit a ti ale unei solut ii de stocare a datelor anume. Vom vedea c a, folosind Java, putem crea aplicat ii care s a ruleze f ar a nici o modicare folosind diverse tipuri de baze care au aceea si structur a, duc and n felul acesta not iunea de portabilitate si mai departe.

Crearea unei baze de date Crearea unei baze de date se face uzual folosind aplicat ii specializate oferite de produc atorul tipului respectiv de sistem de gestiune a datelor, dar exist a si posibilitatea de a crea o baza folosind un script SQL. Acest aspect ne va preocupa ns a mai put in, exemplele prezentate presupun and c a baza a fost creat a deja si are o anumit a structur a specicat 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 aplicat ie si baza de date.

Limbajul SQL SQL (Structured Query Language) reprezint a un limaj de programare ce permite interogarea si actualizarea informat iilor din baze de date relat ionale. Acesta este standardizat astfel nc at diverse tipuri de drivere s a se comporte identic, oferind astfel o modalitate unitar a de lucru cu baze de date.

15.1.2

JDBC

JDBC (Java Database Connectivity) este o interfat a standard SQL de acces la baze de date. JDBC este constituit a dintr-un set de clase si interfet e

DE DATE 15.2. CONECTAREA LA O BAZA

425

scrise n Java, furniz and mecanisme standard pentru proiectant ii aplicat iilor ce folosesc de baze de date. Folosind JDBC este u sor s a transmitem secvent e SQL c atre baze de date relat ionale. Cu alte cuvinte, nu este necesar s a scriem un program pentru a accesa o baz a de date Oracle, alt program pentru a accesa o baz a de date Sybase si asa mai departe. Este de ajuns s a scriem un singur program folosind API-ul JDBC si acesta va capabil s a comunice cu drivere diferite, trimit and secvent e SQL c atre baza de date dorit a. Bine nt eles, scriind codul surs a n Java, ne este asigurat a portabilitatea programului. Deci, iat a dou a motive puternice care fac combinat ia Java - JDBC demn a de luat n seam a. Pachetele care ofer a suport pentru lucrul cu baze de date sunt java.sql ce reprezint a nucleul tehnologiei JDBC si, preluat de pe platforma J2EE, javax.sql. In linii mari, API-ul JDBC ofer a urm atoarele facilit a ti: 1. Stabilirea unei conexiuni cu o baz a de date. 2. Efectuarea de secvent e SQL. 3. Prelucrarea rezultatelor obt inute.

15.2

Conectarea la o baz a de date

Procesul de conectare la o baz a de date implic a efectuarea a dou a operat ii: 1. Inregistrarea unui driver corespunz ator. 2. Realizarea unei conexiuni propriu-zise. Denit ie O conexiune (sesiune) la o baz a de date reprezint a un context prin care sunt trimise secvent e SQL si primite rezultate. Intr-o aplicat ie pot exista simultan mai multe conexiuni la baze de date diferite sau la aceea si baz a. Clasele si interfet ele responsabile cu realizarea unei conexiuni sunt: DriverManager - este clasa ce se ocup a cu nregistrarea driverelor ce vor folosite n aplicat ie;

426

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

Driver - interfat a pe care trebuie s a o implementeze orice clas a ce descrie un driver; DriverPropertyInfo - prin intermediul acestei clase pot specicate diverse propriet a ti ce vor folosite la realizarea conexiunilor; Connection - descrie obiectele ce modeleaz a o conexiune propriu-zis a cu baza de date.

15.2.1

Inregistrarea unui driver

Primul lucru pe care trebuie s a-l fac a o aplicat ie n procesul de conectare la o baz a de date este s a nregistreze la ma sina virtual a ce ruleaz a aplicat ia driverul JDBC responsabil cu comunicarea cu respectiva baz a de date. Acest lucru presupune nc arcarea n memorie a clasei ce implementeaz a driver-ul si poate realizat a n mai multe modalit a ti. a. Folosirea clasei DriverManager: DriverManager.registerDriver(new TipDriver()); b. Folosirea metodei Class.forName ce apeleaz a ClassLoader-ul ma sinii virtuale: Class.forName("TipDriver"); Class.forName("TipDriver").newInstance(); c. Setarea propriet a tii sistem jdbc.drivers, care poate realizat a n dou a feluri: De la linia de comand a: java -Djdbc.drivers=TipDriver Aplicatie Din program: System.setProperty("jdbc.drivers", "TipDriver"); Folosind aceast a metod a, specicarea mai multor drivere se face separ and numele claselor cu punct si virgul a. Dac a sunt nregistrate mai multe drivere, ordinea de precedent a n alegerea driverului folosit la crearea unei noi conexiuni este: 1) Driverele nregistrate folosind proprietatea jdbc.drivers la init ializarea ma sinii virtuale ce va rula procesul. 2) Driverele nregistrate dinamic din aplicat ie.

DE DATE 15.2. CONECTAREA LA O BAZA

427

15.2.2

Specicarea unei baze de date

O dat a ce un driver JDBC a fost nregistrat, acesta poate folosit la stabilirea unei conexiuni cu o baz a de date. Av and n vedere faptul ca pot exista mai multe drivere nc arcate n memorie, trebuie s a avem posibilitea de a specica pe l ang a un identicator al bazei de date si driverul ce trebuie folosit. Aceasta se realizeaz a prin intermediul unei adrese specice, numit a JDBC URL, ce are urm atorul format: jdbc:sub-protocol:identicator C ampul sub-protocol denume ste tipul de driver ce trebuie folosit pentru realizarea conexiunii si poate odbc, oracle, sybase, db2 si a sa mai departe. Identicatorul bazei de date este un indicator specic ec arui driver corespunz ator bazei de date cu care aplicat ia dore ste s a interact ioneze. In funct ie de tipul driver-ului acest identicator poate include numele unei ma sini gazd a, un num ar de port, numele unui sier sau al unui director, etc., ca n exemplele de mai jos: jdbc:odbc:test jdbc:oracle:thin@persistentjava.com:1521:test jdbc:sybase:test jdbc:db2:test Subprotocolul odbc este un caz specical, n sensul c a permite specicarea n cadrul URL-ului a unor atribute ce vor realizate la crearea unei conexiuni. 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 n memorie, p ana c and unul dintre ele va recunoa ste URL-ul respectiv. Dac a nu exista nici unul potrivit, atunci va lansata o except ie de 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 n urm atoarele categorii:

Tip 1. JDBC-ODBC Bridge

Acest tip de driver permite conectarea la o baz a de date care a fost nregistrat a n prealabil n ODBC. ODBC (Open Database Conectivity) reprezint a o modalitate de a uniformiza accesul la baze de date, asociind acestora un identicator DSN (Data Source Name) si diver si parametri necesari conect arii. Conectarea efectiv a la baza de date se va face prin intermediul acestui identicator, driver-ul ODBC efectu and comunicarea cu driverul nativ al bazei de date. De si simplu de utilizat, solut ia JDBC-ODBC nu este portabil a si comunicarea cu baza de date sufer a la nivelul vitezei de execut ie datorit a multiplelor redirect ari ntre drivere. De asemenea, at at ODBC-ul c at si driver-ul nativ trebuie s a existe pe ma sina pe care ruleaz a aplicat ia. Clasa Java care descrie acest tip de driver JDBC este: sun.jdbc.odbc.JdbcOdbcDriver si este inclus a n distribut ia standard J2SDK. Specicarea bazei de date se face printr-un URL de forma: jdbc:odbc:identicator unde identif icator este prolul (DSN) creat bazei de date n ODBC.

Tip 2. Driver JDBC - Driver nativ

DE DATE 15.2. CONECTAREA LA O BAZA

429

Acest tip de driver transform a cererile JDBC direct n apeluri c atre driverul nativ al bazei de date, care trebuie instalat n prealabil. Clase Java care implementeaz a astfel de drivere pot procurate de la produc atorii de SGBD-uri, distribut ia standard J2SDK neincluz and nici unul.

Tip 3. Driver JDBC - Server

Acest tip de driver transform a cererile JDBC folosind un protocol de ret ea independent, acestea ind apoi transormate folosind o aplicat ie server ntr-un protocol specc bazei de date. Introducerea serverului ca nivel intermediar aduce exibilitate maxim a n sensul c a vor putea realizate conexiuni cu diferite tipuri de baze, f ar a nici o modicare la nivelul clientului. Protocolul folosit este specic ec arui produc ator.

Tip 4. Driver JDBC nativ

430

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

Acest tip de driver transform a cererile JDBC direct n cereri c atre baza de date folosind protocolul de ret ea al acesteia. Aceast a solut ie este cea mai rapid a, ind preferat a la dezvoltarea aplicat iilor care manevreaz a volume mari de date si viteza de execut ie este critic a. Drivere de acest tip pot procurate de la diver si produc atori de SGBD-uri.

15.2.4

Realizarea unei conexiuni

Metoda folosit a pentru realizarea unei conexiuni este getConnection din clasa DriverManager si poate avea mai multe forme:

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 SECVENT E 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 a doar schimbarea numelui clasei ce reprezint a driverul si a modalit a tii de specicare a bazei de date. 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 a pentru: Crearea de secvent e SQL utilizate pentru interogarea sau actualizarea bazei. Aarea unor informat ii legate de baza de date (meta-date). De asemenea, clasa Connection asigur a facilit a ti pentru controlul tranzact iilor din memorie c atre baza de date prin metodele commit, rollback, setAutoCommit. Inchiderea unei conexiuni se realizeaz a prin metoda close.

15.3

Efectuarea de secvent e SQL

O dat a facut a conectarea cu metoda DriverManager.getConection, se poate folosi obiectul Connection rezultat pentru a se crea obiecte de tip Statement,PreparedStatement sau CallableStatement cu ajutorul c arora putem trimite secvent e SQL c atre baza de date. Cele mai uzuale comenzi 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 instruct iuni DDL (Data Denition Language) Apelarea unei proceduri stocate: CALL Dup a cum vom vedea, obt inerea si prelucrarea rezultatelor unei interog ari este realizat a prin intermediul obiectelor de tip ResultSet.

15.3.1

Interfat a Statement

Interfat a Statement ofer a metodele de baz a pentru trimiterea de secvent e SQL c atre baza de date si obt inerea rezultatelor, celelalte dou a interfet e: PreparedStatement si CallableStatement ind derivate din aceasta. Crearea unui obiect Statement se realizeaz a prin intermediul metodei createStatement a clasei Connection, f ar a nici un argument: Connection con = DriverManager.getConnection(url); Statement stmt = con.createStatement(); Execut ia unei secvent e SQL poate realizat a prin intermediul a trei metode:

1. executeQuery Este folosit a pentru realizarea de interog ari de tip SELECT. Metoda returneaz a un obiect de tip ResultSet ce va cont ine sub o form a tabelar a rezultatul interog arii. String sql = "SELECT * FROM persoane"; ResultSet rs = stmt.executeQuery(sql);

2. executeUpdate Este folosit a pentru actualizarea datelor (INSERT, UPDATE, DELETE) sau a structurii bazei de date (CREATE, ALTER, DROP). Metoda va returna un ntreg ce semnic a num arul de linii afectate de operat iunea de actualizare a datelor, sau 0 n cazul unei instruct iuni DDL.

15.3. EFECTUAREA DE SECVENT E 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 a metod a va folosit a doar dac a este posibil ca rezultatul unei interog ari s a e format din dou a sau mai multe obiecte de tip ResultSet sau rezultatul unei actualiz ari s a e format din mai mule valori, sau o combinat ie ntre aceste cazuri. Aceast a situat ie, de si mai rar a, este posibil a atunci c and sunt executate proceduri stocate sau secvent e SQL cunoscute abia la momentul execut iei, programatorul ne stiind deci dac a va vorba de o actualizare a datelor sau a structurii. Metoda ntoarce true dac a rezultatul obt inut este format din obiecte de tip ResultSet si false dac a e format din ntregi. In funct ie de aceasta, pot apelate metodele: getResultSet sau getUpdateCount pentru a aa efectiv rezultatul comenzii SQL. Pentru a prelua toate rezultatele va apelat a metoda getMoreResults, dup a care vor apelate din nou metodele amintite, p an a la obt inerea valorii null, respectiv 1. Secvent a complet a de tratare a metodei execute este prezentat a mai 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, n cazul n care dorim s a introducem valorile unor variabile ntr-o secvent a SQL, nu avem alt a solut ie dec at s a cre am un sir de caractere compus din instruct iuni SQL si valorile variabilelor: 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

Interfat a PreparedStatement

Interfat a PreparedStatement este derivat a din Statement, ind diferit a de aceasta n urm atoarele privint e: Instant ele de tip PreparedStatement cont in secvent e SQL care au fost deja compilate (sunt preg atite). O secvent a SQL specicat a unui obiect PreparedStatement poate s a aib a unul sau mai mult i parametri de intrare, care vor specicat i prin intermediul unui semn de ntrebare (?) n locul ec aruia dintre

15.3. EFECTUAREA DE SECVENT E SQL

435

ei. Inainte ca secvent a SQL s a poat a executat a ec arui parametru de intrare trebuie s a i se atribuie o valoare, folosind metode specice acestei clase. Execut ia repetat a a aceleia si secvent e SQL, dar cu parametri diferit i, va n general mai rapid a dac a folosim PreparedStatement, deoarece nu mai trebuie s a cre am c ate un obiect de tip Statement pentru ecare apel SQL, ci refolosim o singur a instant a precompilat a furniz andu-i doar alte argumente. Crearea unui obiect de tip PreparedStatement se realizeaz a prin intermediul metodei prepareStatement a clasei Connection, specic an ca argument o secvent a SQL ce cont ine c ate un semn de 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 cont ine o comand a SQL precompilat a care este trimis a imediat c atre baza de date, unde va a stepta parametri de intrare pentru a putea executat a. Trimiterea parametrilor se realizeaz a prin metode de tip setXXX, unde XXX este tipul corespunz ator parametrului, iar argumentele metodei sunt num arul de ordine al parametrului de intrare (al semnului de ntrebare) si valoarea pe care dorim s a o atribuim. pstmt.setString(1, "Ionescu"); pstmt.setInt(2, 100); Dup a stabilirea parametrilor de intrare secvent a SQL poate executat a. Putem apoi stabili alte valori de intrare si refolosi obiectul PreparedStatement pentru execut ii repetate ale comenzii SQL. Este ns a posibil ca SGBD-ul folosit s a nu suporte acest tip de operat iune si s a nu ret in a obiectul precompilat pentru execut ii ulterioare. In aceast a situat ie folosirea interfet ei PreparedStatement n loc de Statement nu va mbun at a ti n nici un fel performant a codului, din punctul de vedere al vitezei de execut ie a acestuia. Execut ia unei secvent e SQL folosind un obiect PreparedStatement se realizeaz a printr-una din metodele executeQuery, executeUpdate sau execute, semnicat iile lor ind acelea si ca si n cazul obiectelor de tip Statement, cu singura deosebire c a n cazul de fat a ele nu au nici un argument.

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(); Fiec arui tip Java i corespunde un tip generic SQL. Este responsabilitatea programatorului s a se asigure c a folose ste metoda adecvat a de tip setXXX la stabilirea valorii unui parametru de intrare. Lista tuturor tipurilor generice disponibile, numite si tipuri JDBC, este denit a de clasa Types, prin constantelor declarate de aceasta. Metoda setObject permite specicarea unor valori pentru parametrii de intrare, atunci c and dorim s a folosim maparea implicit a ntre tipurile Java si cele JDBC sau atunci c and dorim s a preciz am 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 a atribuim unui parametru de intrare valoare SQL NULL, trebuind ns a s a specic am si tipul de date al coloanei n care vom scrie aceast a valoare. Acela si lucru poate realizat cu metode de tipul setXXX dac a argumentul folosit are valoarea null. 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 ns a situat ii c and este de preferat ca datele de mari dimensiuni s a e transferate pe buc a ti de o anumit a dimensiune. Pentru a realiza

15.3. EFECTUAREA DE SECVENT E SQL

437

acest lucru API-ul JDBC pune la dispozit ie metodele setBinaryStream, setAsciiStream si setUnicodeStream care ata seaz a un ux de intrare pe octet i, caractere ASCII, respectiv UNICODE, unui parametru de intrare. Pe m asur a ce sunt citite date de pe ux, ele vor atribuite parametrului. Exemplul de mai jos ilustreaz a acest lucru, atribuind coloanei continut cont inutul unui anumit sier: 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 execut ia secvent ei, uxul de intrare va apelat repetat pentru a furniza datele ce vor scrise n coloana continut a articolului specicat. Observat i c a este necesar n a s a stim dinainte dimensiunea datelor ce vor scrise, acest lucru ind solicitat de unele tipuri de baze de date.

15.3.3

Interfat a CallableStatement

Interfat a CallableStatement este derivat a din PreparedStatement, instant ele de acest tip oferind o modalitate de a apela o procedur a stocat a ntr-o baz a de date, ntr-o manier a standar pentru toate SGBD-urile. Crearea unui obiect CallableStatement se realizeaz a prin metoda prepareCall 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 a procedura are si parametri de ie sire (valori returnate), ace stia vor trebui nregistrat i cu metoda registerOutParameter nainte de execut ia procedurii. Obt inerea valorilor rezultate n parametrii de ie sie se va face cu metode de tip getXXX. 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 a e si parametru de ie sire. In acest caz el trebuie s a primeasc a o valoare cu setXXX si, de asemenea, va nregistrat cu registerOutParameter, tipurile de date specicate trebuind s a coincid a.

15.3.4 15.3.5

Obt inerea si prelucrarea rezultatelor Interfat a ResultSet

In urma execut ie unei interog ari SQL rezultatul va reprezentat printr-un obiect de tip ResultSet, ce va cont ine toate liniile ce satisfac condit iile impuse de comanda SQL. Forma general a a unui ResultSet este tabelar a, av and un num ar de coloane si de linii, funct ie de secvent a executat a. De asemenea, obiectul va cont ine si meta-datele interog arii cum ar denumirele coloanelor selectate, num arul lor, etc. Statement stmt = con.createStatement(); String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); Rezultatul interog arii de mai sus va obiectul rs cu urm atoarea structur a: cod 100 200 nume Ionescu Popescu

Pentru a extrage informat iile din aceast a structur a va trebui s a parcurgem tabelul linie cu linie si din ecare s a extragem valorile de pe coloane. Pentru acest lucru vom folosi metode de tip getXXX, unde XXX este tipul de dat a al unei coloane iar argumentul primit indic a e num arul de ordine din cadrul tabelului, e numele acestuia. Coloanele sunt numerotate de la st anga la dreapta, ncep and cu 1. In general, folosirea indexului coloanei n loc de numele s au va mai ecient a. De asemenea, pentru maxim a portabilitate se recomand a citirea coloanelor n ordine de la st anga la dreapta si ecare citire s a se fac a o singur a dat a.

15.3. EFECTUAREA DE SECVENT E SQL

439

Un obiect ResultSet folose ste un cursor pentru a parcurge articolele rezultate n urma unei interog ari. Init ial acest cursor este pozit ionat naintea primei linii, ecare apel al metodei next determin and trecerea la urm atoarea linie. Deoarece next returneaz a false c and nu mai sunt linii de adus, uzual va folosit a o bucl a while-loop petru a itera prin articolele tabelului: 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 a dec at nainte, linie cu linie. A sadar, putem itera prin rezultatul unei interog ari o singur a dat a si numai de la prima la ultima linie. Este ns a posibil s a cre am ResultSet-uri care s a permit a modicarea sau deplasarea n ambele sensuri. Exemplul urm ator va folosi un cursor care este modicabil si nu va reecta schimb arile produse de alt i utilizatori dup 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 a un ResultSet folose ste un cursor modicabil si care poate naviga n ambele sensuri, atunci are la dispozit ie o serie de metode ce se bazeaz a pe acest suport: absolute - Deplaseaz a cursorul la o anumit a linie specicat a absolut; updateXXX - Actualizeaz a valoarea unei coloane din linia curent a, unde XXX este un tip de date.

440

CAPITOLUL 15. LUCRUL CU BAZE DE DATE

updateRow - Transfer a actualiz arile f acute liniei n baza de date. moveToInsertRow - deplaseaz a cursorul la o linie spceial a, numit a linie nou a, utilizate a pentru a introduce noi articole n baza de date. Linia curent a anterioar a a cursorului va memorat a pentru a se putea reveni la ea. insertRow - insereaz a articolul din zona linie nou a n baza de date; cursorul trebuie s a e pozit ionat le linia nou a la execut ia acestei operat iuni. moveToCurrentRow - revine la linia curent a din tabel. deleteRow - sterge linia curent a din tabel si din baza de date; nu poate apelat a c and cursorul este n modul linie nou a. Nu toate sistemele de gestiune a bazelor de date ofer a suport pentru folosirea cursoarelor care pot modicate. Pentru a determina dac a baza de date permite acest lucru pot utilizate metodele supportsPositionedUpdate si supportsPositionedDelete ale clasei DatabaseMetaData. In cazul n care acest lucru este permis, este responsabilitatea driver-ului bazei de date s a asigure rezolvarea problemelor legate de actualizarea concurent a a unui cursor, astfel nc at s a nu apar a anomalii.

15.3.6

Exemplu simplu

In continuare vom da un exemplul simplu de utilizare a claselor de baz a ment ionate anterior. Programul va folosi o baz a de date MySql, ce cont ine un tabel numit persoane, av and coloanele: cod, nume si salariu. Scriptul SQL de creare a bazei este: create table persoane(cod integer, nume char(50), salariu double); Aplicat ia va goli tabelul cu persoane, dup a care va ad auga aleator un num ar de articole, va efectua a sarea lor si calculul mediei salariilor. Listing 15.1: Exemplu simplu de utilzare JDBC
import java . sql .*; public class TestJdbc { public static void main ( String [] args ) {

15.3. EFECTUAREA DE SECVENT E SQL


String url = " jdbc : mysql :// localhost / test " ; try { Class . forName ( " com . mysql . jdbc . Driver " ) ; } catch ( Cl as sNo tF oun dE xce pt io 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
Interfat a DatabaseMetaData

Dup a realizarea unui conexiuni la o baz a de date, putem apela metoda getMetaData pentru a aa diverse informat ii legate de baza respectiv a, a sa numitele meta-date (date despre date); Ca rezult al apelului metodei, vom obt ine un obiect de tip DatabaseMetaData ce ofer a metode pentru determinarea tabelelor, procedurilor stocate, capabilit a tilor conexiunii, gramaticii SQL suportate, etc. ale bazei de date. Programul urm ator a seaz a numele tuturor tabelelor dintr-o baz a de dat nregistrat a n ODBC. Listing 15.2: Folosirea interfet ei 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 tF oun dE xc ept io 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

Interfat a ResultSetMetaData

Meta-datele unui ResultSet reprezint a informat iile despre rezultatul cont inut n acel obiect cum ar num arul coloanelor, tipul si denumirile lor, etc. Acestea sunt obt inute apel and metoda getMetaData pentru ResultSet-ul respectiv, care va returna un obiect de tip ResultSetMetaData ce poate apoi folosit pentru extragerea informat iilor 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 Inc arcarea claselor n memorie

Dup a cum stim execut ia unei aplicat ii Java este realizat a de c atre ma sina virtual a Java (JVM), aceasta ind responsabil a cu interpretarea codului de octet i rezultat n urma compil arii. Spre deosebire de alte limbaje de programare cum ar C sau C++, un program Java compilat nu este descris de un sier executabil ci de o mult ime de siere cu extensia .class corespunz atoare ec arei clase a programului. In plus, aceste clase nu sunt n arcate toate n memorie la pornirea aplicat iei, ci sunt n arcate pe parcursul execut ie acesteia atunci c and este nevoie de ele, momentul efectiv n care se realizeaz a acest lucru depinz and de implementarea ma sinii virtuale. Ciclul de viat a al unei clase are a sadar urm atoarele etape: 1. Inc arcarea - Este procesul reg asirii reprezent arii binare a unei clase ( sierul .class) pe baza numelui complet al acestuia si nc arcarea acesteia n memorie. In urma acestui proces, va instant iat un obiect de tip java.lang.Class, corespunz ator clasei respective. Operat iunea de nc arcare a unei clase este realizat a la un moment ce precede prima utilizare efectiv a a sa. 2. Editarea de leg aturi - Specic a incorporarea noului tip de date n JVM pentru a putea utlizat. 3. Init ializarea - Const a n execut ia blocurilor statice de init ializare si init ializarea variabilelor de clas a. 445

446

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

4. Desc arcarea - Atunci c and nu mai exist a nici o referint a de tipul clasei respective, obiectul de tip Class creat va marcat pentru a eliminat din memorie de c atre garbage collector. Inc arcarea claselor unei aplicat ii Java n memorie este realizat a prin intermediul unor obiecte pe care le vom numi generic class loader. Acestea sunt de dou a tipuri: 1. Class loader-ul primordial (eng. bootstrap) - Reprezint a o parte integrant a a ma sinii virtuale, ind responsabil cu nc arcarea claselor standard din distribut ia Java. 2. Class loader-e proprii - Acestea nu fac parte intrinsec a din JVM si sunt instant e ale clasei java.lang.ClassLoader. Aceasta este o clas a abstract a, tipul efectiv al obiectului ind a sadar derivat din aceasta. Dup a cum vom vedea, la execut ia unui program Java vor create implicit dou a obiecte de tip ClassLoader pentru nc arcarea n memorei a claselor proprii ale aplicat iei. Exist a ns a posibilitarea de a crea noi tipuri derivate din ClassLoader specializate pentru nc arcarea claselor conform unor specicat ii anume care s a realizeze diverse optimiz ari. Astfel, nc arcarea unei clase poate determina nc arcarea unor altor clase care sigur vor folosite mpreun a cu prima, sau a unor resurse ce sunt necesare funct ion arii acesteia, etc.

Incep and cu versiunea 1.2 de Java, a fost introdus un model de tip delegat, n care class loader-ele sunt dispuse ierarhic ntr-un arbore, r ad acina acestuia ind class loader-ul primordial. Fiecare instant a de tip ClassLoader va avea a sadar un p arinte (evident, mai put in r ad acina), acesta ind specicat la crearea sa. In momentul c and este solicitat a nc arcarea unei clase, un classloader poate delega n primul r and operat iunea de nc arcare p arintelui s au care va delega la r andul s au cererea mai departe p an a la class loader-ul primordial sau p an a unul din ace stia reu se ste s a o ncarce. Abia n cazul n care nici unul din ace stia nu a reu sit, va ncerca s a execute operat iunea de nc arcare a clasei. Dac a nici ea nu va reu si, va aruncat a o except ie de tipul ClassNotFoundException. De si acest comportament nu este obligatoriu, n multe situat ii el este de preferat, pentru a minimiza nc arcarea aceleia si clase de mai multe ori, folosind class loader-e diferite.

16.1. INCARCAREA CLASELOR IN MEMORIE

447

Implicit, Java 2 JVM ofer a trei class loader-e, unul primordial si dou a proprii, cunoscute sub numele de: Boostrap Class Loader - Class loader-ul primordial. Acesta este responsabil cu nc arcarea claselor din distribut ia Java standard (cele din pachetele java.*, javax.*, etc.). Extension Class Loader - Utilizat pentru nc arcarea claselor din directoarele extensiilor JRE. System Class Loader - Acesta este responsabil cu nc arcarea claselor proprii aplicat iilor Java (cele din CLASSPATH). Tipul acestuia este java.lang.URLClassLoader.

Intruc at tipurile de date Java pot nc arcate folosind diverse instant e de tip ClassLoader, ecare obiect Class va ret ine class loader-ul care a fost folosit pentru nc arcare, acesta put and obt inut cu metoda getClassLoader.

Inc arcarea dinamic a a unei clase n memorie se refer a la faptul c a nu cunoast em tipul acesteia dec at la execut ia preogramului, moment n care putem solicita nc arcarea sa, specic and numele s au complet prin intermediul unui sir de caractere. Acest lucru poate realizat prin mai multe modalit a ti, cele mai comune metode ind:

448

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

loadClass apelat a pentru un obiect de tip ClassLoader ClassLoader loader = new MyClassLoader(); loader.loadClass("NumeCompletClasa"); Class.forName Aceast a metoda va nc arca respectiva clas a folosind class loader-ul 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 a dorim s a instant iem un obiect dintr-o clas a nc arcat a dinamic putem folosi metoda newInstance, cu condit ia s a existe constructorul f ar a argumente pentru clasa respectiv a. Dup a cum vom vedea n sect iunea urm atoare, mai exist a si alte posibilit a ti de a instant ia astfel de obiecte. Class c = Class.forName("java.awt.Button"); Button b = (Button) c.newInstance();

Folosirea interfet elor sau a claselor abstracte mpreun a cu nc arcarea dinamic a a claselor ofer a un mecanism extrem de puternic de lucru n Java. Vom detalia acest lucru prin intermediul unui exepmplu. S a presupunem c a dorim s a cre am o aplicat ie care s a genereze aleator un vector de numere dup a care s a aplice o anumit a funct ie acestui vector. Numele funct iei care trebuie apelat a va introdus de la tastatur a, iar implementarea ei va cont inut a ntr-o clas a a directorului curent. Toate funct iile vor extinde clasa abstract a Funct ie. In felul acesta, aplicat ia poate extins a cu noi funct ii f ar a a schimba codul ei, tot ce trebuie s a facem ind s a scriem noi clase care extind Functie si s a implement am metoda executa. Aceasta va returna 0 dac a metoda s-a executat corect, 1 n caz contrar.

16.1. INCARCAREA CLASELOR IN MEMORIE Listing 16.1: Exemplu de nc arcare dinamic a a claselor
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 dE xce pt io n e ) { System . err . println ( " Functie inexistenta ! " ) ; } catch ( In st ant ia tio nE xce pt io n e ) { System . err . println ( " Functia nu poate fi instantiata ! "); } catch ( Il le gal Ac ces sE xce pt io n e ) { System . err . println ( " Functia nu poate fi accesata ! " ) ;

450
} } } }

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

Listing 16.2: Clasa abstract a ce descrie funct ia


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 funct ie


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 funct ie


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 IN MEMORIE


} }

451

Un obiect de tip URLClassLoader ment ine o list a de adrese URL de unde va ncerca s a ncarce n memorie clasa al c arei nume l specic am ca argument al metodelor de mai sus. Implicit, la crearea class loader-ului aceast a list a este completat a cu informat iile din variabila sistem CLASSPATH sau cu cele specicate prin opt iunea -classpath la lansarea aplicat iei. Folosind metoda getURLs putem aa aceste adrese, iar cu addURL putem ad auga o nou a adres a de c autare a claselor. Bine nt eles, adresele URL pot specica si directoare ale sistemului de siere local. S a presupunem c a n directorul c:\clase\demo exist a clasa cu numele Test, aat a n pachetul demo si dorim s a o nc arc am dinamic n memorie: // 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 a ce o clas a a fost nc arcat a folosind un class loader, ea nu va mai putea desc arcat a explicit din memorie. In cazul n care dorim s a avem posibilitatea de a o re nc arca, deoarece a fost modicat a si recompilat a, trebuie s a folosim class-loadere proprii si s a instant iem noi obiecte de tip ClassLoader, ori de c ate ori dorim s a fort am re nc arcarea claselor. Crearea unui class loader propriu se face uzual prin extinderea clasei URLClassLoader, o variant a simplist a ind prezentat a mai jos: public class MyClassLoader extends URLClassLoader{ public MyClassLoader(URL[] urls){ super(urls); } } Inc arcarea claselor folosind clasa nou creat a se va face astfel:

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 reect arii

Mecanismul prin care o clas a, interfat a sau obiect reect a la momentul execut iei structura lor intern a se nume ste reectare (eng. reection), acesta pun and la dispozit ie metode pentru: Determinarea clasei unui obiect. Aarea unor informat ii despre o clas a (modicatori, superclasa, constructori, metode). Instant ierea unor clase al c aror nume este stiut abia la execut ie. Setarea sau aarea atributelor unui obiect, chiar dac a numele acestora este stiut abia la execut ie. Invocarea metodelor unui obiect al c aror nume este stiut abia la execut ie. Crearea unor vectori a c aror dimensiune si tip nu este stiut dec at la execut ie. Suportul pentru reectare este inclus n distribut ia standard Java, ind cunoscut sub numele de Reection API si cont ine urm atoarele clase:

16.2. MECANISMUL REFLECTARII java.lang.Class java.lang.Object Clasele din pachetul java.lang.reect si anume: Array Constructor Field Method Modifier

453

16.2.1

Examinarea claselor si interfet elor

Examinarea claselor si interfet elor se realizeaz a cu metode ale clasei java.lang.Class, un obiect de acest tip put and s a reprezinte at at o clas a c at si o interfat a, diferent ierea acestora f ac andu-se prin intermediul metodei isInterface. Reection API pune la dispozit ie metode pentru obt inerea urm atoarelor informat ii:

Aarea instant ei Class corespunz ator unui anumit obiect sau tip de date: Class c = obiect.getClass(); Class c = java.awt.Button.class; Class c = Class.forName("NumeClasa"); Tipurile primitive sunt descrise si ele de instant e de tip Class av and forma TipPrimitiv.class: int.class, double.class, etc., diferent ierea lor f ac anduse cu ajutorul metodei isPrimitive.

Aarea numelui unei clase - Se realizeaz a cu metoda getName: Class clasa = obiect.getClass(); String nume = clasa.getName();

454

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

Aarea modicatorilor unei clase - Se realizeaz a cu metoda getModifiers, aceasta return and un num ar ntreg ce codic a tot i modicatorii clasei. Pentru a determina u sor prezent a unui anumit modicator se folosesc metodele statice ale clasei Modifier isPublic, isAbstract si isFinal: 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 a cu metoda getSuperclass ce returneaz a o instant a de tip Class, corespunz atoare tipului de date al superclasei 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 interfet elor implementate de o clas a sau extinse de o interfat a - Se realizeaz a cu metoda getInterfaces, ce returneaz a un vector de tip 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 a cu una din metodele getFields sau getDeclaredFields, ce returnez a un vector de tip Field[], diferent a ntre cele dou a const and n faptul c a prima returneaz a toate variabilele membre, inclusiv cele mo stenite, n timp ce a doua le returnez a doar pe cele declarate n cadrul clasei. La r andul ei, clasa Field pune la dispozit ie metodele getName, getType si getModifiers pentru a obt ine numele, tipul, respectiv modicatorii unei variabile membru. Cu ajutorul metodei getField este posibil a obt inerea unei referint e la o variabil a mebr a cu un anumit nume specicat.

Aarea constructorilor - Se realizeaz a cu metodele getConstructors sau getDeclaredConstructors, ce returneaz a un vector de tip Constructor[]. Clasa Constructor pune la dispozit ie metodele getName, getParameterTypes, getModifiers, getExceptionTypes pentru a obt ine toate informat iile legate de respectivul constructor. Cu ajutorul metodei getConstructor este posibil a obt inerea unei referint e la constructor cu o signatur a specicat a.

Aarea metodelor - Se realizeaz a cu metodele getMethods sau getDeclaredMethods, ce returneaz a un vector de tip Method[]. Clasa Method pune la dispozit ie metodele getName, getParameterTypes, getModifiers, getExceptionTypes, getReturnType pentru a obt ine toate informat iile legate

456

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

de respectiva metod a. Cu ajutorul metodei getMethod este posibil a obt inerea unei referint e la o metod a cu o signatur a specicat a.

Aarea claselor imbricate - Se realizeaz a cu metodele getClasses sau getDeclaredClasses, ce returnez a un vector de tip Class[].

Aarea clasei de acoperire - Se realizeaz a cu metoda getDeclaringClass. Aceast a metod a o reg asim si n clasele Field, Constructor, Method, pentru acestea return and clasa c arei i apart ine variabila, constructorul sau metoda respectiv a.

16.2.2

Manipularea obiectelor

Pe l ang a posibilitatea de a examina structura unei anumite clase la momentul execut iei, folosind Reection API avem posibilitatea de a lucra dinamic cu obiecte, baz andu-ne pe informat ii pe care le obt inem abia la execut ie.

Crearea obiectelor Dup a cum st im, crearea obiectelor se realizeaz a cu operatorul new urmat de un apel la un constructor al clasei pe care o instant iem. In cazul n care numele clasei nu este cunoscut dec at la momentul execut iei nu mai putem folosi aceast a metod a de instant iere. In schimb, avem la dispozit ie alte dou a variante: Metoda newInstance din clasa java.lang.Class Aceasta permite instant ierea unui obiect folosind constructorul f ar a argumente al acestuia. Dac a nu exist a un astfel de constructor sau nu este accesibil vor generate except ii de tipul InstantiationException, respectiv 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 instant ierea unui obiect folosind un anumit constructor, cel pentru care se face apelul. Evident, aceast a solut ie presupune n primul r and obt inerea unui obiect de tip Constructor cu o anumit a signatur a, apoi specicarea argumentelor la apelarea sa. S a rescriem exemplul de mai sus, apel and constructorul cu dou a argumente al clasei 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); Except ii generate de metoda newInstance sunt: InstantiationException, IllegalAccessException, IllegalArgumentException si InvocationTargetException. Metoda getConstructor poate provoca except ii de tipul NoSuchMethodException.

Invocarea metodelor Invocarea unei metode al c arei nume l cunoa stem abia la momentul execut iei se realizeaz a cu metoda invoke a clasei Method. Ca si n cazul constructorilor, trebuie s a obt inem nt ai o referint a la metoda cu signatura corespunz atoare si apoi s a specic am argumentele. In plus, mai putem obt ine valoarea returnat a. S a presupunem c a dorim s a apel am metoda contains

458

CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

a clasei Rectangle care determin a dac a un anumit punct se g ase ste n interiorul drepunghiului. Metoda contains are mai multe variante, noi o vom apela pe cea care accept a un argument de tip Point. 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 a num arul argumentelor metodei este 0, atunci putem folosi valoarea null n locul vectorilor ce reprezint a signatura, respectiv parametri de apelare ai metodei. Except iile generate de metoda invoke sunt: IllegalAccessException si InvocationTargetException. Metoda getMethod poate provoca except ii de tipul NoSuchMethodException.

Setarea si aarea variabilelor membre Pentur setarea si aarea valorilor variabilelor membre sunt folosite metodele set si get ale clasei Field. S a presupunem c a dorim s a set am variabila x a unui obiect de tip Point si s a obt inem valoarea variabilei y a aceluia si obiect: 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

Except iile generate de metodele get, set sunt: IllegalAccessException si IllegalArgumentException. Metoda getField poate provoca except ii de tipul NoSuchFieldException.

Revenind la exemplul din sect iunea anterioar a cu apelarea dinamic a a unor funct ii pentru un vector, s a presupunem c a exist a deja un num ar nsemnat de clase care descriu diferite funct ii dar acestea nu extind clasa abstract a Functie. Din acest motiv, solut ia anterioar a nu mai este viabil a si trebuie s a folosim apelarea metodei executa ntr-un mod dinamic. Listing 16.5: Lucru dinamic cu metode si variabile
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 reprezentat i ca tip de date tot prin intermediul clasei java.lang.Class diferent ierea f ac andu-se prin intermediul metodei isArray. Tipul de date al elementelor din care este format vectorul va obt inut cu ajutorul metodei getComponentType, ce ntoarce o referint a de tip Class. 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 a vectori se realizeaz a prin intermediul clasei Array. Aceasta cont ine o serie de metode statice ce permit: Crearea de noi vectori: newInstance Aarea num arului de elemente: getLength

16.2. MECANISMUL REFLECTARII Setarea / aarea elementelor: set, get

461

Exemplul de mai jos creeaz a un vector ce cont ine numerele 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) + " ");