Cristian frasinaru curs-practic_de_java
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
1,887
On Slideshare
1,887
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
12
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Curs practic de Java Cristian Fr˘sinaru a
  • 2. Cuprins1 Introducere ˆ Java ın 11 1.1 Ce este Java ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.1.1 Limbajul de programare Java . . . . . . . . . . . . . . 11 1.1.2 Platforme de lucru Java . . . . . . . . . . . . . . . . . 12 1.1.3 Java: un limbaj compilat ¸i interpretat s . . . . . . . . . 13 1.2 Primul program . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3 Structura lexical˘ a limbajului Java . . . . . . a . . . . . . . . . 16 1.3.1 Setul de caractere . . . . . . . . . . . . . . . . . . . . . 16 1.3.2 Cuvinte cheie . . . . . . . . . . . . . . . . . . . . . . . 16 1.3.3 Identificatori . . . . . . . . . . . . . . . . . . . . . . . . 17 1.3.4 Literali . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.3.5 Separatori . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.3.6 Operatori . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.3.7 Comentarii . . . . . . . . . . . . . . . . . . . . . . . . 20 1.4 Tipuri de date ¸i variabile . . . . . . . . . . . s . . . . . . . . . 21 1.4.1 Tipuri de date . . . . . . . . . . . . . . . . . . . . . . . 21 1.4.2 Variabile . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1.5 Controlul executiei . . . . . . . . . . . . . . . ¸ . . . . . . . . . 24 1.5.1 Instructiuni de decizie . . . . . . . . . ¸ . . . . . . . . . 24 1.5.2 Instructiuni de salt . . . . . . . . . . . ¸ . . . . . . . . . 25 1.5.3 Instructiuni pentru tratarea exceptiilor ¸ ¸ . . . . . . . . . 26 1.5.4 Alte instructiuni . . . . . . . . . . . . ¸ . . . . . . . . . 26 1.6 Vectori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 1.6.1 Crearea unui vector . . . . . . . . . . . . . . . . . . . . 26 1.6.2 Tablouri multidimensionale . . . . . . . . . . . . . . . 28 1.6.3 Dimensiunea unui vector . . . . . . . . . . . . . . . . . 28 1.6.4 Copierea vectorilor . . . . . . . . . . . . . . . . . . . . 29 1
  • 3. 2 CUPRINS 1.6.5 Sortarea vectorilor - clasa Arrays . . . . . . . . . . . . 29 1.6.6 Vectori cu dimensiune variabil˘ ¸i eterogeni as . . . . . . 30 1.7 Siruri de caractere . . . . . . . . . . . . . . . . . . ¸ . . . . . . 30 1.8 Folosirea argumentelor de la linia de comand˘ . . . a . . . . . . 31 1.8.1 Transmiterea argumentelor . . . . . . . . . . . . . . . . 31 1.8.2 Primirea argumentelor . . . . . . . . . . . . . . . . . . 32 1.8.3 Argumente numerice . . . . . . . . . . . . . . . . . . . 342 Obiecte ¸i clase s 35 2.1 Ciclul de viat˘ al unui obiect . . . . . . . . . . . . . ¸a . . . . . . 35 2.1.1 Crearea obiectelor . . . . . . . . . . . . . . . . . . . . . 35 2.1.2 Folosirea obiectelor . . . . . . . . . . . . . . . . . . . . 37 2.1.3 Distrugerea obiectelor . . . . . . . . . . . . . . . . . . 38 2.2 Crearea claselor . . . . . . . . . . . . . . . . . . . . . . . . . . 39 2.2.1 Declararea claselor . . . . . . . . . . . . . . . . . . . . 39 2.2.2 Extinderea claselor . . . . . . . . . . . . . . . . . . . . 40 2.2.3 Corpul unei clase . . . . . . . . . . . . . . . . . . . . . 41 2.2.4 Constructorii unei clase . . . . . . . . . . . . . . . . . . 42 2.2.5 Declararea variabilelor . . . . . . . . . . . . . . . . . . 46 2.2.6 this ¸i super . . . . . . . . . . . . . . . . . . s . . . . . . 49 2.3 Implementarea metodelor . . . . . . . . . . . . . . . . . . . . 50 2.3.1 Declararea metodelor . . . . . . . . . . . . . . . . . . . 50 2.3.2 Tipul returnat de o metod˘ . . . . . . . . . a . . . . . . 52 2.3.3 Trimiterea parametrilor c˘tre o metod˘ . . . a a . . . . . . 53 2.3.4 Metode cu num˘r variabil de argumente . . a . . . . . . 56 2.3.5 Supraˆ arcarea ¸i supradefinirea metodelor ınc˘ s . . . . . . 57 2.4 Modificatori de acces . . . . . . . . . . . . . . . . . . . . . . . 58 2.5 Membri de instant˘ ¸i membri de clas˘ . . . . . . . ¸a s a . . . . . . 59 2.5.1 Variabile de instant˘ ¸i de clas˘ . . . . . . . ¸a s a . . . . . . 59 2.5.2 Metode de instant˘ ¸i de clas˘ . . . . . . . . ¸a s a . . . . . . 61 2.5.3 Utilitatea membrilor de clas˘ . . . . . . . . a . . . . . . 62 2.5.4 Blocuri statice de initializare . . . . . . . . . ¸ . . . . . . 63 2.6 Clase imbricate . . . . . . . . . . . . . . . . . . . . . . . . . . 64 2.6.1 Definirea claselor imbricate . . . . . . . . . . . . . . . . 64 2.6.2 Clase interne . . . . . . . . . . . . . . . . . . . . . . . 66 2.6.3 Identificare claselor imbricate . . . . . . . . . . . . . . 66 2.6.4 Clase anonime . . . . . . . . . . . . . . . . . . . . . . . 67 2.7 Clase ¸i metode abstracte . . . . . . . . . . . . . . s . . . . . . 67
  • 4. CUPRINS 3 2.7.1 Declararea unei clase abstracte . . . . . . . . . . . . . 68 2.7.2 Metode abstracte . . . . . . . . . . . . . . . . . . . . . 68 2.8 Clasa Object . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 2.8.1 Orice clas˘ are o superclas˘ . . a a . . . . . . . . . . . . . 71 2.8.2 Clasa Object . . . . . . . . . . . . . . . . . . . . . . . 71 2.9 Conversii automate ˆıntre tipuri . . . . . . . . . . . . . . . . . 74 2.10 Tipul de date enumerare . . . . . . . . . . . . . . . . . . . . . 753 Exceptii ¸ 77 3.1 Ce sunt exceptiile ? . . . . . . . . . . . . ¸ . . . . . . . . . . . . 77 3.2 ”Prinderea” ¸i tratarea exceptiilor . . . . s ¸ . . . . . . . . . . . . 78 3.3 ”Aruncarea” exceptiilor . . . . . . . . . . ¸ . . . . . . . . . . . . 82 3.4 Avantajele trat˘rii exceptiilor . . . . . . a ¸ . . . . . . . . . . . . 85 3.4.1 Separarea codului pentru tratarea erorilor . . . . . . . 85 3.4.2 Propagarea erorilor . . . . . . . . . . . . . . . . . . . . 87 3.4.3 Gruparea erorilor dup˘ tipul lor . a . . . . . . . . . . . . 89 3.5 Ierarhia claselor ce descriu exceptii . . . ¸ . . . . . . . . . . . . 90 3.6 Exceptii la executie . . . . . . . . . . . . ¸ ¸ . . . . . . . . . . . . 91 3.7 Crearea propriilor exceptii . . . . . . . . ¸ . . . . . . . . . . . . 924 Intr˘ri ¸i ie¸iri a s s 95 4.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.1.1 Ce sunt fluxurile? . . . . . . . . . . . . . . . . . . . . . 95 4.1.2 Clasificarea fluxurilor . . . . . . . . . . . . . . . . . . . 96 4.1.3 Ierarhia claselor pentru lucrul cu fluxuri . . . . . . . . 97 4.1.4 Metode comune fluxurilor . . . . . . . . . . . . . . . . 98 4.2 Folosirea fluxurilor . . . . . . . . . . . . . . . . . . . . . . . . 99 4.2.1 Fluxuri primitive . . . . . . . . . . . . . . . . . . . . . 99 4.2.2 Fluxuri de procesare . . . . . . . . . . . . . . . . . . . 100 4.2.3 Crearea unui flux . . . . . . . . . . . . . . . . . . . . . 101 4.2.4 Fluxuri pentru lucrul cu fi¸iere . . . . . . . . . . s . . . . 103 4.2.5 Citirea ¸i scrierea cu buffer . . . . . . . . . . . . s . . . . 105 4.2.6 Concatenarea fluxurilor . . . . . . . . . . . . . . . . . . 107 4.2.7 Fluxuri pentru filtrarea datelor . . . . . . . . . . . . . 108 4.2.8 Clasele DataInputStream ¸i DataOutputStream s . . . . 109 4.3 Intr˘ri ¸i ie¸iri formatate . . . . . . . . . . . . . . . . . a s s . . . . 110 4.3.1 Intr˘ri formatate . . . . . . . . . . . . . . . . . a . . . . 110 4.3.2 Ie¸iri formatate . . . . . . . . . . . . . . . . . . s . . . . 111
  • 5. 4 CUPRINS 4.4 Fluxuri standard de intrare ¸i ie¸ire . . . . . . . . . . . . . s s . . 111 4.4.1 Afisarea informatiilor pe ecran . . . . . . . . . . . . ¸ . . 112 4.4.2 Citirea datelor de la tastatur˘ . . . . . . . . . . . . a . . 112 4.4.3 Redirectarea fluxurilor standard . . . . . . . . . . . . . 113 4.4.4 Analiza lexical˘ pe fluxuri (clasa StreamTokenizer) a . . 115 4.5 Clasa RandomAccesFile (fi¸iere cu acces direct) . . . . . . s . . 117 4.6 Clasa File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1195 Interfete ¸ 121 5.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 5.1.1 Ce este o interfat˘ ? . . . . . . . . . ¸a . . . . . . . . . . 121 5.2 Folosirea interfetelor . . . . . . . . . . . . . ¸ . . . . . . . . . . 122 5.2.1 Definirea unei interfete . . . . . . . . ¸ . . . . . . . . . . 122 5.2.2 Implementarea unei interfete . . . . . ¸ . . . . . . . . . . 123 5.2.3 Exemplu: implementarea unei stive . . . . . . . . . . . 124 5.3 Interfete ¸i clase abstracte . . . . . . . . . . ¸ s . . . . . . . . . . 129 5.4 Mo¸tenire multipl˘ prin interfete . . . . . . s a ¸ . . . . . . . . . . 130 5.5 Utilitatea interfetelor . . . . . . . . . . . . . ¸ . . . . . . . . . . 132 5.5.1 Crearea grupurilor de constante . . . . . . . . . . . . . 132 5.5.2 Transmiterea metodelor ca parametri . . . . . . . . . . 133 5.6 Interfata FilenameFilter . . . . . . . . . . ¸ . . . . . . . . . . 134 5.6.1 Folosirea claselor anonime . . . . . . . . . . . . . . . . 137 5.7 Compararea obiectelor . . . . . . . . . . . . . . . . . . . . . . 138 5.7.1 Interfata Comparable . . . . . . . . . ¸ . . . . . . . . . . 139 5.7.2 Interfata Comparator . . . . . . . . . ¸ . . . . . . . . . . 141 5.8 Adaptori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1426 Organizarea claselor 145 6.1 Pachete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 6.1.1 Pachetele standard (J2SDK) . . . . . . . . . . . . . . . 145 6.1.2 Folosirea membrilor unui pachet . . . . . . . . . . . . . 146 6.1.3 Importul unei clase sau interfete . ¸ . . . . . . . . . . . . 147 6.1.4 Importul la cerere dintr-un pachet . . . . . . . . . . . . 148 6.1.5 Importul static . . . . . . . . . . . . . . . . . . . . . . 149 6.1.6 Crearea unui pachet . . . . . . . . . . . . . . . . . . . 150 6.1.7 Denumirea unui pachet . . . . . . . . . . . . . . . . . . 151 6.2 Organizarea fi¸ierelor . . . . . . . . . . . s . . . . . . . . . . . . 152 6.2.1 Organizarea fi¸ierelor surs˘ . . . . s a . . . . . . . . . . . . 152
  • 6. CUPRINS 5 6.2.2 Organizarea unit˘¸ilor de compilare (.class) at . . . . . 154 6.2.3 Necesitatea organiz˘rii fi¸ierelor . . . . . . . . a s . . . . . 155 6.2.4 Setarea c˘ii de c˘utare (CLASSPATH) . . . . a a . . . . . 156 6.3 Arhive JAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 6.3.1 Folosirea utilitarului jar . . . . . . . . . . . . . . . . . 158 6.3.2 Executarea aplicatiilor arhivate . . . . . . . . ¸ . . . . . 1597 Colectii ¸ 161 7.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 7.2 Interfete ce descriu colectii . . ¸ ¸ . . . . . . . . . . . . . . . . . . 162 7.3 Implement˘ri ale colectiilor . . a ¸ . . . . . . . . . . . . . . . . . . 166 7.4 Folosirea eficient˘ a colectiilor a ¸ . . . . . . . . . . . . . . . . . . 168 7.5 Algoritmi polimorfici . . . . . . . . . . . . . . . . . . . . . . . 170 7.6 Tipuri generice . . . . . . . . . . . . . . . . . . . . . . . . . . 171 7.7 Iteratori ¸i enumer˘ri . . . . . s a . . . . . . . . . . . . . . . . . . 1728 Serializarea obiectelor 177 8.1 Folosirea serializ˘rii . . . . . . . . . . . . . . . a . . . . . . . . . 177 8.1.1 Serializarea tipurilor primitive . . . . . . . . . . . . . . 179 8.1.2 Serializarea obiectelor . . . . . . . . . . . . . . . . . . . 180 8.1.3 Clasa ObjectOutputStream . . . . . . . . . . . . . . . 180 8.1.4 Clasa ObjectInputStream . . . . . . . . . . . . . . . . 181 8.2 Obiecte serializabile . . . . . . . . . . . . . . . . . . . . . . . . 183 8.2.1 Implementarea interfetei Serializable . ¸ . . . . . . . . . 183 8.2.2 Controlul serializ˘rii . . . . . . . . . . a . . . . . . . . . 184 8.3 Personalizarea serializ˘rii obiectelor . . . . . . a . . . . . . . . . 187 8.3.1 Controlul versiunilor claselor . . . . . . . . . . . . . . . 188 8.3.2 Securizarea datelor . . . . . . . . . . . . . . . . . . . . 193 8.3.3 Implementarea interfetei Externalizable ¸ . . . . . . . . . 194 8.4 Clonarea obiectelor . . . . . . . . . . . . . . . . . . . . . . . . 1969 Interfata grafic˘ cu utilizatorul ¸ a 199 9.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 9.2 Modelul AWT . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 9.2.1 Componentele AWT . . . . . . . . . . . . . . . . . . . 202 9.2.2 Suprafete de afi¸are (Clasa Container) ¸ s . . . . . . . . . 204 9.3 Gestionarea pozition˘rii . . . . . . . . . . . . ¸ a . . . . . . . . . 206 9.3.1 Folosirea gestionarilor de pozitionare . ¸ . . . . . . . . . 207
  • 7. 6 CUPRINS 9.3.2 Gestionarul FlowLayout . . . . . . . . . . . . . . . . . 209 9.3.3 Gestionarul BorderLayout . . . . . . . . . . . . . . . . 210 9.3.4 Gestionarul GridLayout . . . . . . . . . . . . . . . . . 211 9.3.5 Gestionarul CardLayout . . . . . . . . . . . . . . . . . 212 9.3.6 Gestionarul GridBagLayout . . . . . . . . . . . . . . . 214 9.3.7 Gruparea componentelor (Clasa Panel) . . . . . . . . . 218 9.4 Tratarea evenimentelor . . . . . . . . . . . . . . . . . . . . . . 219 9.4.1 Exemplu de tratare a evenimentelor . . . . . . . . . . . 221 9.4.2 Tipuri de evenimente . . . . . . . . . . . . . . . . . . . 224 9.4.3 Folosirea adaptorilor ¸i a claselor anonime . s . . . . . . 227 9.5 Folosirea ferestrelor . . . . . . . . . . . . . . . . . . . . . . . . 232 9.5.1 Clasa Window . . . . . . . . . . . . . . . . . . . . . . . 232 9.5.2 Clasa Frame . . . . . . . . . . . . . . . . . . . . . . . . 233 9.5.3 Clasa Dialog . . . . . . . . . . . . . . . . . . . . . . . . 236 9.5.4 Clasa FileDialog . . . . . . . . . . . . . . . . . . . . . 239 9.6 Folosirea meniurilor . . . . . . . . . . . . . . . . . . . . . . . . 242 9.6.1 Ierarhia claselor ce descriu meniuri . . . . . . . . . . . 243 9.6.2 Tratarea evenimentelor generate de meniuri . . . . . . 246 9.6.3 Meniuri de context (popup) . . . . . . . . . . . . . . . 247 9.6.4 Acceleratori (Clasa MenuShortcut) . . . . . . . . . . . 250 9.7 Folosirea componentelor AWT . . . . . . . . . . . . . . . . . . 250 9.7.1 Clasa Label . . . . . . . . . . . . . . . . . . . . . . . . 251 9.7.2 Clasa Button . . . . . . . . . . . . . . . . . . . . . . . 252 9.7.3 Clasa Checkbox . . . . . . . . . . . . . . . . . . . . . . 253 9.7.4 Clasa CheckboxGroup . . . . . . . . . . . . . . . . . . 255 9.7.5 Clasa Choice . . . . . . . . . . . . . . . . . . . . . . . 257 9.7.6 Clasa List . . . . . . . . . . . . . . . . . . . . . . . . . 259 9.7.7 Clasa ScrollBar . . . . . . . . . . . . . . . . . . . . . . 261 9.7.8 Clasa ScrollPane . . . . . . . . . . . . . . . . . . . . . 262 9.7.9 Clasa TextField . . . . . . . . . . . . . . . . . . . . . . 263 9.7.10 Clasa TextArea . . . . . . . . . . . . . . . . . . . . . . 26510 Desenarea 269 10.1 Conceptul de desenare . . . . . . . . . . . . . . . . . . . . . . 269 10.1.1 Metoda paint . . . . . . . . . . . . . . . . . . . . . . . 270 10.1.2 Suprafete de desenare - clasa Canvas ¸ . . . . . . . . . . 271 10.2 Contextul grafic de desenare . . . . . . . . . . . . . . . . . . . 274 10.2.1 Propriet˘¸ile contextului grafic . . . . at . . . . . . . . . . 275
  • 8. CUPRINS 7 10.2.2 Primitive grafice . . . . . . . . . . . . . . . . . . . . . 275 10.3 Folosirea fonturilor . . . . . . . . . . . . . . . . . . . . . . . . 276 10.3.1 Clasa Font . . . . . . . . . . . . . . . . . . . . . . . . . 277 10.3.2 Clasa FontMetrics . . . . . . . . . . . . . . . . . . . . . 279 10.4 Folosirea culorilor . . . . . . . . . . . . . . . . . . . . . . . . . 282 10.5 Folosirea imaginilor . . . . . . . . . . . . . . . . . . . . . . . . 286 10.5.1 Afi¸area imaginilor . . . . . . . . . s . . . . . . . . . . . 287 10.5.2 Monitorizarea ˆ arc˘rii imaginilor ınc˘ a . . . . . . . . . . . 289 10.5.3 Mecanismul de ”double-buffering” . . . . . . . . . . . . 291 10.5.4 Salvarea desenelor ˆ format JPEG ın . . . . . . . . . . . 291 10.5.5 Crearea imaginilor ˆ memorie . . ın . . . . . . . . . . . 292 10.6 Tip˘rirea . . . . . . . . . . . . . . . . . . . a . . . . . . . . . . . 29311 Swing 299 11.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 11.1.1 JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 11.1.2 Swing API . . . . . . . . . . . . . . . . . . . . . . . . . 300 11.1.3 Asem˘n˘ri ¸i deosebiri cu AWT . . a a s . . . . . . . . . . . 301 11.2 Folosirea ferestrelor . . . . . . . . . . . . . . . . . . . . . . . . 304 11.2.1 Ferestre interne . . . . . . . . . . . . . . . . . . . . . . 305 11.3 Clasa JComponent . . . . . . . . . . . . . . . . . . . . . . . . 307 11.4 Arhitectura modelului Swing . . . . . . . . . . . . . . . . . . . 310 11.5 Folosirea modelelor . . . . . . . . . . . . . . . . . . . . . . . . 310 11.5.1 Tratarea evenimentelor . . . . . . . . . . . . . . . . . . 314 11.6 Folosirea componentelor . . . . . . . . . . . . . . . . . . . . . 316 11.6.1 Componente atomice . . . . . . . . . . . . . . . . . . . 316 11.6.2 Componente pentru editare de text . . . . . . . . . . . 316 11.6.3 Componente pentru selectarea unor elemente . . . . . . 319 11.6.4 Tabele . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 11.6.5 Arbori . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 11.6.6 Containere . . . . . . . . . . . . . . . . . . . . . . . . . 332 11.6.7 Dialoguri . . . . . . . . . . . . . . . . . . . . . . . . . 335 11.7 Desenarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 11.7.1 Metode specifice . . . . . . . . . . . . . . . . . . . . . 336 11.7.2 Consideratii generale . . . . . . . . ¸ . . . . . . . . . . . 338 11.8 Look and Feel . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
  • 9. 8 CUPRINS12 Fire de executie ¸ 343 12.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 12.2 Crearea unui fir de executie . . . . . . . . ¸ . . . . . . . . . . . 344 12.2.1 Extinderea clasei Thread . . . . . . . . . . . . . . . . . 345 12.2.2 Implementarea interfetei Runnable ¸ . . . . . . . . . . . 347 12.3 Ciclul de viat˘ al unui fir de executie . . . ¸a ¸ . . . . . . . . . . . 352 12.3.1 Terminarea unui fir de executie . . ¸ . . . . . . . . . . . 355 12.3.2 Fire de executie de tip ”daemon” . ¸ . . . . . . . . . . . 357 12.3.3 Stabilirea priorit˘¸ilor de executie . at ¸ . . . . . . . . . . . 358 12.3.4 Sincronizarea firelor de executie . . ¸ . . . . . . . . . . . 362 12.3.5 Scenariul produc˘tor / consumator a . . . . . . . . . . . 362 12.3.6 Monitoare . . . . . . . . . . . . . . . . . . . . . . . . . 367 12.3.7 Semafoare . . . . . . . . . . . . . . . . . . . . . . . . . 369 12.3.8 Probleme legate de sincronizare . . . . . . . . . . . . . 371 12.4 Gruparea firelor de executie . . . . . . . . ¸ . . . . . . . . . . . 373 12.5 Comunicarea prin fluxuri de tip ”pipe” . . . . . . . . . . . . . 376 12.6 Clasele Timer ¸i TimerTask . . . . . . . . s . . . . . . . . . . . 37813 Programare ˆ retea ın ¸ 383 13.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 13.2 Lucrul cu URL-uri . . . . . . . . . . . . . . . . . . . . . . . . 385 13.3 Socket-uri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 13.4 Comunicarea prin conexiuni . . . . . . . . . . . . . . . . . . . 388 13.5 Comunicarea prin datagrame . . . . . . . . . . . . . . . . . . . 393 13.6 Trimiterea de mesaje c˘tre mai multi clienti a ¸ ¸ . . . . . . . . . . 39714 Appleturi 401 14.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 14.2 Crearea unui applet simplu . . . . . . . . . . . . . . . . . . . . 402 14.3 Ciclul de viat˘ al unui applet . . . . . ¸a . . . . . . . . . . . . . 404 14.4 Interfata grafic˘ cu utilizatorul . . . . . ¸ a . . . . . . . . . . . . . 406 14.5 Definirea ¸i folosirea parametrilor . . . s . . . . . . . . . . . . . 408 14.6 Tag-ul APPLET . . . . . . . . . . . . . . . . . . . . . . . . . 410 14.7 Folosirea firelor de executie ˆ appleturi ¸ ın . . . . . . . . . . . . . 412 14.8 Alte metode oferite de clasa Applet . . . . . . . . . . . . . . . 416 14.9 Arhivarea appleturilor . . . . . . . . . . . . . . . . . . . . . . 420 14.10Restrictii de securitate . . . . . . . . . ¸ . . . . . . . . . . . . . 421 14.11Appleturi care sunt ¸i aplicatii . . . . . s ¸ . . . . . . . . . . . . . 421
  • 10. CUPRINS 915 Lucrul cu baze de date 423 15.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 15.1.1 Generalit˘¸i despre baze de date . . . at . . . . . . . . . . 423 15.1.2 JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 15.2 Conectarea la o baz˘ de date . . . . . . . . . a . . . . . . . . . . 425 15.2.1 Inregistrarea unui driver . . . . . . . . . . . . . . . . . 426 15.2.2 Specificarea unei baze de date . . . . . . . . . . . . . . 427 15.2.3 Tipuri de drivere . . . . . . . . . . . . . . . . . . . . . 428 15.2.4 Realizarea unei conexiuni . . . . . . . . . . . . . . . . 430 15.3 Efectuarea de secvente SQL . . . . . . . . . ¸ . . . . . . . . . . 431 15.3.1 Interfata Statement . . . . . . . . . . ¸ . . . . . . . . . . 432 15.3.2 Interfata PreparedStatement . . . . . ¸ . . . . . . . . . . 434 15.3.3 Interfata CallableStatement . . . . . ¸ . . . . . . . . . . 437 15.3.4 Obtinerea ¸i prelucrarea rezultatelor ¸ s . . . . . . . . . . 438 15.3.5 Interfata ResultSet . . . . . . . . . . ¸ . . . . . . . . . . 438 15.3.6 Exemplu simplu . . . . . . . . . . . . . . . . . . . . . . 440 15.4 Lucrul cu meta-date . . . . . . . . . . . . . . . . . . . . . . . 442 15.4.1 Interfata DatabaseMetaData . . . . . ¸ . . . . . . . . . . 442 15.4.2 Interfata ResultSetMetaData . . . . ¸ . . . . . . . . . . 44316 Lucrul dinamic cu clase 445 16.1 Inc˘rcarea claselor ˆ memorie . . . . . . a ın . . . . . . . . . . . . 445 16.2 Mecanismul reflect˘rii . . . . . . . . . . a . . . . . . . . . . . . 452 16.2.1 Examinarea claselor ¸i interfetelor s ¸ . . . . . . . . . . . . 453 16.2.2 Manipularea obiectelor . . . . . . . . . . . . . . . . . . 456 16.2.3 Lucrul dinamic cu vectori . . . . . . . . . . . . . . . . 460
  • 11. 10 CUPRINS
  • 12. Capitolul 1Introducere ˆ Java ın1.1 Ce este Java ?Java este o tehnologie inovatoare lansat˘ de compania Sun Microsystems ˆ a ın1995, care a avut un impact remarcabil asupra ˆ ıntregii comunit˘¸i a dez- atvoltatorilor de software, impunˆndu-se prin calit˘¸i deosebite cum ar fi sim- a atplitate, robustete ¸i nu ˆ ultimul rˆnd portabilitate. Denumit˘ initial OAK, ¸ s ın a a ¸tehnologia Java este format˘ dintr-un limbaj de programare de nivel ˆ a ınalt pebaza c˘ruia sunt construite o serie de platforme destinate implement˘rii de a aaplicatii pentru toate segmentele industriei software. ¸1.1.1 Limbajul de programare JavaInainte de a prezenta ˆ detaliu aspectele tehnice ale limbajului Java, s˘ am- ın aintim caracteristicile sale principale, care l-au transformat ˆ ıntr-un interval detimp atˆt de scurt ˆ a ıntr-una din cele mai pupulare optiuni pentru dezvoltarea ¸de aplicatii, indiferent de domeniu sau de complexitatea lor. ¸ • Simplitate - elimin˘ supraˆ arcarea operatorilor, mo¸tenirea multipl˘ a ınc˘ s a ¸i toate ”facilit˘¸ile” ce pot provoca scrierea unui cod confuz. s at • U¸urint˘ ˆ crearea de aplicatii complexe ce folosesc programarea ˆ s ¸a ın ¸ ın retea, fire de executie, interfat˘ grafic˘, baze de date, etc. ¸ ¸ ¸a a • Robustete - elimin˘ sursele frecvente de erori ce apar ˆ programare ¸ a ın prin renuntarea la pointeri, administrarea automat˘ a memoriei ¸i elim- ¸ a s 11
  • 13. 12 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN inarea pierderilor de memorie printr-o procedur˘ de colectare a obiectelor a care nu mai sunt referite, ce ruleaz˘ ˆ fundal (”garbage collector”). a ın • Complet orientat pe obiecte - elimin˘ complet stilul de programare a procedural. • Securitate - este un limbaj de programare foarte sigur, furnizˆnd a mecanisme stricte de securitate a programelor concretizate prin: ver- ificarea dinamic˘ a codului pentru detectarea secventelor periculoase, a ¸ impunerea unor reguli stricte pentru rularea proceselor la distant˘, etc. ¸a • Neutralitate arhitectural˘ - comportamentul unei aplicatii Java nu a ¸ depinde de arhitectura fizic˘ a ma¸inii pe care ruleaz˘. a s a • Portabililtate - Java este un limbaj independent de platforma de lu- cru, aceea¸i aplicatie rulˆnd f˘r˘ nici o modificare ¸i f˘r˘ a necesita re- s ¸ a aa s aa compilarea ei pe sisteme de operare diferite cum ar fi Windows, Linux, Mac OS, Solaris, etc. lucru care aduce economii substantiale firmelor ¸ dezvoltatoare de aplicatii. ¸ • Este compilat ¸i interpretat, aceasta fiind solutia eficient˘ pentru s ¸ a obtinerea portabilit˘¸ii. ¸ at • Performant˘ - de¸i mai lent decˆt limbajele de programare care genereaz˘ ¸a s a a executabile native pentru o anumit˘ platform˘ de lucru, compilatorul a a Java asigur˘ o performant˘ ridicat˘ a codului de octeti, astfel ˆ at a ¸a a ¸ ıncˆ viteza de lucru putin mai sc˘zut˘ nu va fi un impediment ˆ dezvoltarea ¸ a a ın de aplicatii oricˆt de complexe, inclusiv grafic˘ 3D, animatie, etc. ¸ a a ¸ • Este modelat dup˘ C ¸i C++, trecerea de la C, C++ la Java a s f˘cˆndu-se foarte u¸or. a a s1.1.2 Platforme de lucru JavaLimbajul de programare Java a fost folosit la dezvoltarea unor tehnologii ded-icate rezolv˘rii unor probleme din cele mai diverse domenii. Aceste tehnologii aau fost grupate ˆ a¸a numitele platforme de lucru, ce reprezint˘ seturi de ın s alibr˘rii scrise ˆ limbajul Java, precum ¸i diverse programe utilitare, folosite a ın spentru dezvoltarea de aplicatii sau componente destinate unei anume cate- ¸gorii de utilizatori.
  • 14. 1.1. CE ESTE JAVA ? 13 • J2SE (Standard Edition) Este platforma standard de lucru ce ofer˘ suport pentru crearea de a aplicatii independente ¸i appleturi. ¸ s De asemenea, aici este inclus˘ ¸i tehnologia Java Web Start ce furnizeaz˘ as a o modalitate extrem de facil˘ pentru lansarea ¸i instalarea local˘ a pro- a s a gramelor scrise ˆ Java direct de pe Web, oferind cea mai comod˘ solutie ın a ¸ pentru distributia ¸i actualizarea aplicatiilor Java. ¸ s ¸ • J2ME (Micro Edition) Folosind Java, programarea dispozitivelor mobile este extrem de simpl˘, a platforma de lucru J2ME oferind suportul necesar scrierii de programe dedicate acestui scop. • J2EE (Enterprise Edition) Aceast˘ platform˘ ofer˘ API-ul necesar dezvolt˘rii de aplicatii com- a a a a ¸ plexe, formate din componente ce trebuie s˘ ruleze ˆ sisteme eterogene, a ın cu informatiile memorate ˆ baze de date distribuite, etc. ¸ ın Tot aici g˘sim ¸i suportul necesar pentru crearea de aplicatii ¸i servicii a s ¸ s Web, bazate pe componente cum ar fi servleturi, pagini JSP, etc. Toate distributiile Java sunt oferite gratuit ¸i pot fi desc˘rcate de pe ¸ s aInternet de la adresa ”http://java.sun.com”. In continuare, vom folosi termenul J2SDK pentru a ne referi la distributia ¸standard J2SE 1.5 SDK (Tiger).1.1.3 Java: un limbaj compilat ¸i interpretat sIn functie de modul de executie a aplicatiilor, limbajele de programare se ¸ ¸ ¸ˆımpart ˆ dou˘ categorii: ın a • Interpretate: instructiunile sunt citite linie cu linie de un program ¸ numit interpretor ¸i traduse ˆ instructiuni ma¸in˘. Avantajul aces- s ın ¸ s a tei solutii este simplitatea ¸i faptul c˘ fiind interpretat˘ direct sursa ¸ s a a programului obtinem portabilitatea. Dezavantajul evident este viteza ¸ de executie redus˘. Probabil cel mai cunoscute limbaj interpretat este ¸ a limbajul Basic. • Compilate: codul surs˘ al programelor este transformat de compi- a lator ˆ ıntr-un cod ce poate fi executat direct de procesor, numit cod
  • 15. 14 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN ma¸in˘. Avantajul este executia extrem de rapid˘, dezavantajul fiind s a ¸ a lipsa portabilit˘¸ii, codul compilat ˆ at ıntr-un format de nivel sc˘zut nu a poate fi rulat decˆt pe platforma de lucru pe care a fost compilat. a Limbajul Java combin˘ solutiile amintite mai sus, programele Java fiind a ¸atˆt interpretate cˆt ¸i compilate. A¸adar vom avea la dispozitie un compi- a a s s ¸lator responsabil cu transformarea surselor programului ˆ a¸a numitul cod ın sde octeti, precum ¸i un interpretor ce va executa respectivul cod de octeti. ¸ s ¸ Codul de octeti este diferit de codul ma¸in˘. Codul ma¸in˘ este reprezen- ¸ s a s atat de o succesiune de instructiuni specifice unui anumit procesor ¸i unei an- ¸ sumite platforme de lucru reprezentate ˆ format binar astfel ˆ at s˘ poat˘ ın ıncˆ a afi executate f˘r˘ a mai necesita nici o prelucrare. aa Codurile de octeti sunt seturi de instructiuni care seam˘n˘ cu codul scris ¸ ¸ a aˆ limbaj de asamblare ¸i sunt generate de compilator independent de mediulın sde lucru. In timp ce codul ma¸in˘ este executat direct de c˘tre procesor ¸i s a a spoate fi folosit numai pe platforma pe care a fost creat, codul de octeti este ¸interpretat de mediul Java ¸i de aceea poate fi rulat pe orice platform˘ pe s acare este instalat˘ mediul de executie Java. a ¸ Prin ma¸ina virtual˘ Java (JVM) vom ˆ ¸elege mediul de executie al s a ınt ¸aplicatiilor Java. Pentru ca un cod de octeti s˘ poat˘ fi executat pe un ¸ ¸ a aanumit calculator, pe acesta trebuie s˘ fie instalat˘ o ma¸in˘ virtual˘ Java. a a s a aAcest lucru este realizat automat de c˘tre distributia J2SDK. a ¸1.2 Primul programCrearea oric˘rei aplicatii Java presupune efectuarea urm˘torilor pa¸i: a ¸ a s 1. Scriererea codului surs˘ aclass FirstApp { public static void main( String args[]) { System.out.println("Hello world!"); }}
  • 16. 1.2. PRIMUL PROGRAM 15 Toate aplicatiile Java contin o clas˘ principal˘(primar˘) ˆ care trebuie ¸ ¸ a a a ıns˘ se gaseasc˘ metoda main. Clasele aplicatiei se pot gasi fie ˆ a a ¸ ıntr-un singurfi¸ier, fie ˆ mai multe. s ın 2. Salvarea fi¸ierelor surs˘ s a Se va face ˆ fi¸iere care au obligatoriu extensia java, nici o alt˘ exten- ın s asie nefiind acceptat˘. Este recomandat ca fi¸ierul care contine codul surs˘ a s ¸ aal clasei primare s˘ aib˘ acela¸i nume cu cel al clasei, de¸i acest lucru nu a a s seste obligatoriu. S˘ presupunem c˘ am salvat exemplul de mai sus ˆ fi¸ierul a a ın sC:introFirstApp.java. 3. Compilarea aplicatiei ¸ Pentru compilare vom folosi compilatorul javac din distributia J2SDK. ¸Apelul compilatorului se face pentru fi¸ierul ce contine clasa principal˘ a s ¸ aaplicatiei sau pentru orice fi¸ier/fi¸iere cu extensia java. Compilatorul creeaz˘ ¸ s s acˆte un fi¸ier separat pentru fiecare clas˘ a programului. Acestea au extensia a s a.class ¸i implicit sunt plasate ˆ acela¸i director cu fi¸ierele surs˘. s ın s s a javac FirstApp.javaIn cazul ˆ care compilarea a reu¸it va fi generat fi¸ierul FirstApp.class. ın s s 4. Rularea aplicatiei ¸ Se face cu interpretorul java, apelat pentru unitatea de compilare core-spunz˘toare clasei principale. Deoarece interpretorul are ca argument de aintrare numele clasei principale ¸i nu numele unui fi¸ier, ne vom pozitiona s s ¸ˆ directorul ce contine fi¸ierul FirstApp.class ¸i vom apela interpretorulın ¸ s sastfel: java FirstAppRularea unei aplicatii care nu folose¸te interfat˘ grafic˘, se va face ˆ ¸ s ¸a a ıntr-ofereastr˘ sistem. a
  • 17. 16 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN Atentie ¸ Un apel de genul java c:introFirstApp.class este gre¸it! s1.3 Structura lexical˘ a limbajului Java a1.3.1 Setul de caractereLimbajului Java lucreaz˘ ˆ mod nativ folosind setul de caractere Unicode. a ınAcesta este un standard international care ˆ ¸ ınlocuie¸te vechiul set de caractere sASCII ¸i care folose¸te pentru reprezentarea caracterelor 2 octeti, ceea ce s s ¸ˆınseamn˘ c˘ se pot reprezenta 65536 de semne, spre deosebire de ASCII, unde a aera posibil˘ reprezentarea a doar 256 de caractere. Primele 256 caractere aUnicode corespund celor ASCII, referirea la celelalte f˘cˆndu-se prin uxxxx, a aunde xxxx reprezint˘ codul caracterului. a O alt˘ caracteristic˘ a setului de caractere Unicode este faptul c˘ ˆ a a a ıntregintervalul de reprezentare a simbolurilor este divizat ˆ subintervale numite ınblocuri, cˆteva exemple de blocuri fiind: Basic Latin, Greek, Arabic, Gothic, aCurrency, Mathematical, Arrows, Musical, etc. Mai jos sunt oferite cˆteva exemple de caractere Unicode. a • u0030 - u0039 : cifre ISO-Latin 0 - 9 • u0660 - u0669 : cifre arabic-indic 0 - 9 • u03B1 - u03C9 : simboluri grece¸ti α − ω s • u2200 - u22FF : simboluri matematice (∀, ∃, ∅, etc.) • u4e00 - u9fff : litere din alfabetul Han (Chinez, Japonez, Coreean) Mai multe informatii legate de reprezentarea Unicode pot fi obtinute la ¸ ¸adresa ”http://www.unicode.org”.1.3.2 Cuvinte cheieCuvintele rezervate ˆ Java sunt, cu cˆteva exceptii, cele din C++ ¸i au fost ın a ¸ senumerate ˆ tabelul de mai jos. Acestea nu pot fi folosite ca nume de clase, ın
  • 18. ˘1.3. STRUCTURA LEXICALA A LIMBAJULUI JAVA 17interfete, variabile sau metode. true, false, null nu sunt cuvinte cheie, ¸dar nu pot fi nici ele folosite ca nume ˆ aplicatii. Cuvintele marcate prin ∗ ın ¸sunt rezervate, dar nu sunt folosite. abstract double int strictfp boolean else interface super break extends long switch byte final native synchronized case finally new this catch float package throw char for private throws class goto* protected transient const* if public try continue implements return void default import short volatile do instanceof static while Incepˆnd cu versiunea 1.5, mai exist˘ ¸i cuvˆntul cheie enum. a as a1.3.3 IdentificatoriSunt secvente nelimitate de litere ¸i cifre Unicode, ˆ ¸ s ıncepˆnd cu o liter˘. Dup˘ a a acum am mai spus, identificatorii nu au voie s˘ fie identici cu cuvintele rezer- avate.1.3.4 LiteraliLiteralii pot fi de urm˘toarele tipuri: a • Intregi Sunt acceptate 3 baze de numeratie : baza 10, baza 16 (ˆ ¸ ıncep cu car- acterele 0x) ¸i baza 8 (ˆ s ıncep cu cifra 0) ¸i pot fi de dou˘ tipuri: s a – normali - se reprezint˘ pe 4 octeti (32 biti) a ¸ ¸ – lungi - se reprezint˘ pe 8 octeti (64 biti) ¸i se termin˘ cu caracterul a ¸ ¸ s a L (sau l).
  • 19. 18 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN • Flotanti ¸ Pentru ca un literal s˘ fie considerat flotant el trebuie s˘ aib˘ cel putin o a a a ¸ zecimal˘ dup˘ virgul˘, s˘ fie ˆ notatie exponential˘ sau s˘ aib˘ sufixul a a a a ın ¸ ¸ a a a F sau f pentru valorile normale - reprezentate pe 32 biti, respectiv D ¸ sau d pentru valorile duble - reprezentate pe 64 biti. ¸ Exemple: 1.0, 2e2, 3f, 4D. • Logici Sunt reprezentati de true - valoarea logic˘ de adev˘r, respectiv false ¸ a a - valoarea logic˘ de fals. a Atentie ¸ Spre deosebire de C++, literalii ˆ ıntregi 1 ¸i 0 nu mai au semnificatia s ¸ de adev˘rat, respectiv fals. a • Caracter Un literal de tip caracter este utilizat pentru a exprima caracterele co- dului Unicode. Reprezentarea se face fie folosind o liter˘, fie o secvent˘ a ¸a escape scris˘ ˆ a ıntre apostrofuri. Secventele escape permit specificarea ¸ caracterelor care nu au reprezentare grafic˘ ¸i reprezentarea unor car- as actere speciale precum backslash, apostrof, etc. Secventele escape pre- ¸ definite ˆ Java sunt: ın – ’b’ : Backspace (BS) – ’t’ : Tab orizontal (HT) – ’n’ : Linie nou˘ (LF) a – ’f’ : Pagin˘ nou˘ (FF) a a – ’r’ : Inceput de rˆnd (CR) a – ’"’ : Ghilimele – ’’’ : Apostrof – ’’ : Backslash
  • 20. ˘1.3. STRUCTURA LEXICALA A LIMBAJULUI JAVA 19 • Siruri de caractere ¸ Un literal ¸ir de caractere este format din zero sau mai multe caractere s ˆ ıntre ghilimele. Caracterele care formeaz˘ ¸irul pot fi caractere grafice as sau secvente escape. ¸ Dac˘ ¸irul este prea lung el poate fi scris ca o concatenare de sub¸iruri as s de dimensiune mai mic˘, concatenarea ¸irurilor realizˆndu-se cu oper- a s a atorul +, ca ˆ exemplul: "Ana " + " are " + " mere ". Sirul vid ın este "". Dup˘ cum vom vedea, orice ¸ir este de fapt o instant˘ a clasei String, a s ¸a definit˘ ˆ pachetul java.lang. a ın1.3.5 SeparatoriUn separator este un caracter care indic˘ sfˆr¸itul unei unit˘¸i lexicale ¸i a as at sınceputul alteia. In Java separatorii sunt urm˘torii: ( ) a [ ] ; , . .Instructiunile unui program se separ˘ cu punct ¸i virgul˘. ¸ a s a1.3.6 OperatoriOperatorii Java sunt, cu mici deosebiri, cei din C++: • atribuirea: = • operatori matematici: +, -, *, /, %, ++, -- . Este permis˘ notatia prescurtat˘ de forma lval op= rval: x += 2 n a ¸ a -= 3 Exist˘ operatori pentru autoincrementare ¸i autodecrementare (post ¸i a s s pre): x++, ++x, n--, --n Evaluarea expresiilor logice se face prin metoda scurtcircuitului: evalu- area se opre¸te ˆ momentul ˆ care valoarea de adev˘r a expresiei este s ın ın a sigur determinat˘. a • operatori logici: &&(and), ||(or), !(not) • operatori relationali: <, <=, >, <=, ==, != ¸ • operatori pe biti: &(and), |(or), ^ (xor), ~ (not) ¸ • operatori de translatie: <<, >>, >>> (shift la dreapta f˘r˘ semn) ¸ a a
  • 21. 20 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN • operatorul if-else: expresie-logica ? val-true : val-false • operatorul , (virgul˘) folosit pentru evaluarea secvential˘ a operatiilor: a ¸ a ¸ int x=0, y=1, z=2; • operatorul + pentru concatenarea ¸irurilor: s String s1="Ana"; String s2="mere"; int x=10; System.out.println(s1 + " are " + x + " " + s2); • operatori pentru conversii (cast) : (tip-de-data) int a = (int)’a’; char c = (char)96; int i = 200; long l = (long)i; //widening conversion long l2 = (long)200; int i2 = (int)l2; //narrowing conversion1.3.7 ComentariiIn Java exist˘ trei feluri de comentarii: a • Comentarii pe mai multe linii, ˆ ınchise ˆ ıntre /* ¸i */. s • Comentarii pe mai multe linii care ¸in de documentatie, ˆ t ¸ ınchise ˆ ıntre /** ¸i */. Textul dintre cele dou˘ secvente este automat mutat ˆ s a ¸ ın documentatia aplicatiei de c˘tre generatorul automat de documentatie ¸ ¸ a ¸ javadoc. • Comentarii pe o singur˘ linie, care incep cu //. a Observatii: ¸ • Nu putem scrie comentarii ˆ interiorul altor comentarii. ın • Nu putem introduce comentarii ˆ interiorul literalilor caracter sau ¸ir ın s de caractere. • Secventele /* ¸i */ pot s˘ apar˘ pe o linie dup˘ secventa // dar ˆsi ¸ s a a a ¸ ı¸ pierd semnificatia. La fel se ˆ ¸ ıntampl˘ cu secventa // ˆ comentarii care a ¸ ın incep cu /* sau */.
  • 22. 1.4. TIPURI DE DATE SI VARIABILE ¸ 211.4 Tipuri de date ¸i variabile s1.4.1 Tipuri de dateIn Java tipurile de date se impart ˆ dou˘ categorii: tipuri primitive ¸i ın a stipuri referint˘. Java porne¸te de la premiza c˘ ”orice este un obiect”, ¸a s aprin urmare tipurile de date ar trebui s˘ fie de fapt definite de clase ¸i toate a svariabilele ar trebui s˘ memoreze instante ale acestor clase (obiecte). In a ¸principiu acest lucru este adev˘rat, ˆ a, pentru usurinta program˘rii, mai a ıns˘ ¸ aexist˘ ¸i a¸a numitele tipurile primitive de date, care sunt cele uzuale : as s • aritmetice –ˆ ıntregi: byte (1 octet), short (2), int (4), long (8) – reale: float (4 octeti), double (8) • caracter: char (2 octeti) ¸ • logic: boolean (true ¸i false) sIn alte limbaje de programare formatul ¸i dimensiunea tipurilor primitive de sdate pot depinde de platforma pe care ruleaz˘ programul. In Java acest lucru anu mai este valabil, orice dependent˘ de o anumit˘ platform˘ specific˘ fiind ¸a a a aeliminat˘. a Vectorii, clasele ¸i interfetele sunt tipuri referint˘. Valoarea unei variabile s ¸ ¸ade acest tip este, spre deosebire de tipurile primitive, o referint˘ (adres˘ de ¸a amemorie) c˘tre valoarea sau multimea de valori reprezentat˘ de variabila a ¸ arespectiv˘. a Exist˘ trei tipuri de date din limbajul C care nu sunt suportate de lim- abajul Java. Acestea sunt: pointer, struct ¸i union. Pointerii au fost seliminati din cauz˘ c˘ erau o surs˘ constant˘ de erori, locul lor fiind luat de ¸ a a a atipul referint˘, iar struct ¸i union nu ˆsi mai au rostul atˆt timp cˆt tipurile ¸a s ı¸ a acompuse de date sunt formate ˆ Java prin intermediul claselor. ın
  • 23. 22 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN1.4.2 VariabileVariabilele pot fi de tip primitiv sau referinte la obiecte (tip referint˘). In- ¸ ¸adiferent de tipul lor, pentru a putea fi folosite variabilele trebuie declarate ¸i, seventual, initializate. ¸ • Declararea variabilelor: Tip numeVariabila; • Initializarea variabilelor: Tip numeVariabila = valoare; ¸ • Declararea constantelor: final Tip numeVariabila; Evident, exist˘ posibilitatea de a declara ¸i initializa mai multe variabile a s ¸sau constante de acela¸i tip ˆ s ıntr-o singur˘ instructiune astfel: a ¸ Tip variabila1[=valoare1], variabila2[=valoare2],...; Conventia de numire a variabilelor ˆ Java include, printre altele, urm˘toarele ¸ ın acriterii: • variabilele finale (constante) se scriu cu majuscule; • variabilele care nu sunt constante se scriu astfel: prima liter˘ mic˘ iar a a dac˘ numele variabilei este format din mai multi atomi lexicali, atunci a ¸ primele litere ale celorlalti atomi se scriu cu majuscule. ¸ Exemple: final double PI = 3.14; final int MINIM=0, MAXIM = 10; int valoare = 100; char c1=’j’, c2=’a’, c3=’v’, c4=’a’; long numarElemente = 12345678L; String bauturaMeaPreferata = "apa"; In functie de locul ˆ care sunt declarate variabilele se ˆ ¸ ın ımpart ˆ urm˘toatele ın acategorii: a. Variabile membre, declarate ˆ interiorul unei clase, vizibile pentru ın toate metodele clasei respective cˆt ¸i pentru alte clase ˆ functie de a s ın ¸ nivelul lor de acces (vezi ”Declararea variabilelor membre”).
  • 24. 1.4. TIPURI DE DATE SI VARIABILE ¸ 23 b. Parametri metodelor, vizibili doar ˆ metoda respectiv˘. ın a c. Variabile locale, declarate ˆ ıntr-o metod˘, vizibile doar ˆ metoda re- a ın spectiv˘. a d. Variabile locale, declarate ˆ ıntr-un bloc de cod, vizibile doar ˆ blocul ın respectiv. e. Parametrii de la tratarea exceptiilor (vezi ”Tratarea exceptiilor”). ¸ ¸class Exemplu { //Fiecare variabila corespunde situatiei data de numele ei //din enumerarea de mai sus int a; public void metoda(int b) { a = b; int c = 10; for(int d=0; d < 10; d++) { c --; } try { a = b/c; } catch(ArithmeticException e) { System.err.println(e.getMessage()); } }} Observatii: • Variabilele declarate ˆ ıntr-un for, r˘mˆn locale corpului ciclului: a a for(int i=0; i<100; i++) { //domeniul de vizibilitate al lui i } i = 101;//incorect • Nu este permis˘ ascunderea unei variabile: a
  • 25. 24 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN int x=1; { int x=2; //incorect }1.5 Controlul executiei ¸Instructiunile Java pentru controlul executiei sunt foarte asem˘n˘toare celor ¸ ¸ a adin limbajul C ¸i pot fi ˆ artite ˆ urm˘toarele categorii: s ımp˘ ¸ ın a • Instructiuni de decizie: if-else, switch-case ¸ • Instructiuni de salt: for, while, do-while ¸ • Instructiuni pentru tratarea exceptiilor: try-catch-finally, throw ¸ ¸ • Alte instructiuni: break, continue, return, label: ¸1.5.1 Instructiuni de decizie ¸if-elseif (expresie-logica) { ...}if (expresie-logica) { ...} else { ...} switch-caseswitch (variabila) { case valoare1: ... break; case valoare2:
  • 26. 1.5. CONTROLUL EXECUTIEI ¸ 25 ... break; ... default: ...} Variabilele care pot fi testate folosind instructiunea switch nu pot fi decˆt ¸ ade tipuri primitive.1.5.2 Instructiuni de salt ¸forfor(initializare; expresie-logica; pas-iteratie) { //Corpul buclei}for(int i=0, j=100 ; i < 100 && j > 0; i++, j--) { ...}Atˆt la initializare cˆt ¸i ˆ pasul de iteratie pot fi mai multe instructiuni a ¸ a s ın ¸ ¸desp˘rtite prin virgul˘. a¸ a whilewhile (expresie-logica) { ...} do-whiledo { ...}while (expresie-logica);
  • 27. 26 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN1.5.3 Instructiuni pentru tratarea exceptiilor ¸ ¸Instructiunile pentru tratarea exceptiilor sunt try-catch-finally, respectiv ¸ ¸throw ¸i vor fi tratate ˆ capitolul ”Exceptii”. s ın ¸1.5.4 Alte instructiuni ¸ • break: p˘r˘se¸te fortat corpul unei structuri repetitive. aa s ¸ • continue: termina fortat iteratia curent˘ a unui ciclu ¸i trece la urm˘toarea ¸ ¸ a s a iteratie. ¸ • return [valoare]: termin˘ o metod˘ ¸i, eventual, returneaz˘ o valo- a as a rare. • numeEticheta: : Define¸te o etichet˘. s a De¸i ˆ Java nu exist˘ goto, se pot defini totu¸i etichete folosite ˆ expresii s ın a s ınde genul: break numeEticheata sau continue numeEticheta, utile pentrua controla punctul de ie¸ire dintr-o structur˘ repetitiv˘, ca ˆ s a a ınexemplul demai 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 Vectori1.6.1 Crearea unui vectorCrearea unui vector presupune realizarea urm˘toarelor etape: a
  • 28. 1.6. VECTORI 27 • Declararea vectorului - Pentru a putea utiliza un vector trebuie, ˆ ınainte de toate, sa-l declar˘m. Acest lucru se face prin expresii de forma: a Tip[] numeVector; sau Tip numeVector[]; ca ˆ exemplele de mai jos: ın int[] intregi; String adrese[]; • Instantierea ¸ Declararea unui vector nu implic˘ ¸i alocarea memoriei necesare pentru as retinerea elementelor. Operatiunea de alocare a memoriei, numit˘ ¸i ¸ ¸ a s instantierea vectorului, se realizeaz˘ ˆ ¸ a ıntotdeauna prin intermediul op- eratorului new. Instantierea unui vector se va face printr-o expresie de ¸ genul: numeVector = new Tip[nrElemente]; unde nrElemente reprezint˘ num˘rul maxim de elemente pe care le a a poate avea vectorul. In urma instantierii vor fi alocati: nrElemente ∗ ¸ ¸ dimensiune(T ip) octeti necesari memor˘rii elementelor din vector, unde ¸ a prin dimensiune(T ip) am notat num˘rul de octeti pe care se reprezint˘ a ¸ a tipul respectiv. v = new int[10]; //aloca spatiu pentru 10 intregi: 40 octeti c = new char[10]; //aloca spatiu pentru 10 caractere: 20 octeti Declararea ¸i instantierea unui vector pot fi f˘cute simultan astfel: s ¸ a Tip[] numeVector = new Tip[nrElemente];
  • 29. 28 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN • Initializarea (optional) Dup˘ declararea unui vector, acesta poate fi ¸ ¸ a initializat, adic˘ elementele sale pot primi ni¸te valori initiale, evident ¸ a s ¸ dac˘ este cazul pentru a¸a ceva. In acest caz instantierea nu mai trebuie a s ¸ facut˘ explicit, alocarea memoriei f˘cˆndu-se automat ˆ functie de a a a ın ¸ num˘ rul de elemente cu care se initializeaz˘ vectorul. a ¸ a String culori[] = {"Rosu", "Galben", "Verde"}; int []factorial = {1, 1, 2, 6, 24, 120}; Primul indice al unui vector este 0, deci pozitiile unui vector cu n ele- ¸ ıntre 0 ¸i n − 1. Nu sunt permise constructii de genulmente vor fi cuprinse ˆ s ¸Tip numeVector[nrElemente], alocarea memoriei f˘cˆndu-se doar prin in- a atermediul opearatorului new. int v[10]; //ilegal int v[] = new int[10]; //corect1.6.2 Tablouri multidimensionaleIn Java tablourile multidimensionale sunt de fapt vectori de vectori. Deexemplu, crearea ¸i instantierea unei matrici vor fi realizate astfel: s ¸ Tip matrice[][] = new Tip[nrLinii][nrColoane]; matrice[i] este linia i a matricii ¸i reprezint˘ un vector cu nrColoane s aelemente iar matrice[i][j] este elementul de pe linia i ¸i coloana j. s1.6.3 Dimensiunea unui vectorCu ajutorul variabilei length se poate afla num˘rul de elemente al unui avector. int []a = new int[5]; // a.length are valoarea 5 int m[][] = new int[5][10]; // m[0].length are valoarea 10Pentru a ˆ ¸elege modalitatea de folosire a lui length trebuie mentionat c˘ ınt ¸ afiecare vector este de fapt o instant˘ a unei clase iar length este o variabil˘ ¸a apublic˘ a acelei clase, ˆ care este retinut num˘rul maxim de elemente al a ın ¸ avectorului.
  • 30. 1.6. VECTORI 291.6.4 Copierea vectorilorCopierea elementelor unui vector a ˆıntr-un alt vector b se poate face, fieelement cu element, fie cu ajutorul metodei System.arraycopy, ca ˆ exem- ınplele de mai jos. Dup˘ cum vom vedea, o atribuire de genul b = a are alt˘ a asemnificatie decˆt copierea elementelor lui a ˆ b ¸i nu poate fi folosit˘ ˆ ¸ a ın s a ınacest 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 ArraysIn Java s-a pus un accent deosebit pe implementarea unor structuri de date¸i algoritmi care s˘ simplifice proceseul de crearea a unui algoritm, program-s aatorul trebuind s˘ se concentreze pe aspectele specifice problemei abordate. aClasa java.util.Arrays ofer˘ diverse metode foarte utile ˆ lucrul cu vec- a ıntori cum ar fi: • sort - sorteaz˘ ascendent un vector, folosind un algoritm de tip Quick- a Sort performant, de complexitate O(n log(n)). int v[]={3, 1, 4, 2}; java.util.Arrays.sort(v); // Sorteaza vectorul v // Acesta va deveni {1, 2, 3, 4} • binarySearch - c˘utarea binar˘ a unei anumite valori ˆ a a ıntr-un vector sortat;
  • 31. 30 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN • equals - testarea egalit˘¸ii valorilor a doi vectori (au acelea¸i num˘r at s a de elemente ¸i pentru fiecare indice valorile corespunz˘toare din cei doi s a vectori sunt egale) • fill - atribuie fiec˘rui element din vector o valoare specificat˘. a a1.6.6 Vectori cu dimensiune variabil˘ ¸i eterogeni asImplement˘ri ale vectorilor cu num˘r variabil de elemente sunt oferite de a aclase specializate cum ar fi Vector sau ArrayList din pachetul java.util.Astfel de obiecte descriu vectori eterogeni, ale c˘ror elemente au tipul Object, a¸i vor fi studiati ˆ capitolul ”Colectii”.s ¸ ın ¸1.7 Siruri de caractere ¸In Java, un ¸ir de caractere poate fi reprezentat printr-un vector format sdin elemente de tip char, un obiect de tip String sau un obiect de tipStringBuffer. Dac˘ un ¸ir de caractere este constant (nu se dore¸te schimbarea continutului a s s ¸s˘ pe parcursul executiei programului) atunci el va fi declarat de tipul String, a ¸altfel va fi declarat de tip StringBuffer. Diferenta principal˘ ˆ ¸ a ıntre acesteclase este c˘ StringBuffer pune la dispozitie metode pentru modificarea a ¸continutului ¸irului, cum ar fi: append, insert, delete, reverse. ¸ sUzual, cea mai folosit˘ modalitate de a lucra cu ¸iruri este prin intermediul a sclasei String, care are ¸i unele particularit˘¸i fat˘ de restul claselor menite s˘ s at ¸a asimplifice cˆt mai mult folosirea ¸irurilor de caractere. Clasa StringBuffer a sva fi utilizat˘ predominant ˆ aplicatii dedicate proces˘rii textelor cum ar fi a ın ¸ aeditoarele de texte. Exemple echivalente de declarare a unui ¸ir:s String s = "abc"; String s = new String("abc"); char data[] = {’a’, ’b’, ’c’}; String s = new String(data); Observati prima variant˘ de declarare a ¸irului s din exemplul de mai sus ¸ a s- de altfel, cea mai folosit˘ - care prezint˘ o particularitate a clasei String a afata de restul claselor Java referitoare la instantierea obiectelor sale. ¸ ¸
  • 32. ˘1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA 31 Concatenarea ¸irurilor de caractere se face prin intermediul operatorului s+ sau, ˆ cazul ¸irurilor de tip StringBuffer, folosind metoda append. ın s String s1 = "abc" + "xyz"; String s2 = "123"; String s3 = s1 + s2;In Java, operatorul de concatenare + este extrem de flexibil, ˆ sensul c˘ ın apermite concatenarea ¸irurilor cu obiecte de orice tip care au o reprezentare sde tip ¸ir de caractere. Mai jos, sunt cˆteva exemple: s a System.out.print("Vectorul v are" + v.length + " elemente"); String x = "a" + 1 + "b"Pentru a l˘muri putin lucrurile, ceea ce execut˘ compilatorul atunci cˆnd a ¸ a aˆ alne¸te o secvent˘ de genul String x = "a" + 1 + "b" este:ıntˆ s ¸a String x = new StringBuffer().append("a").append(1). append("b").toString() Atentie ˆ a la ordinea de efectuare a operatiilor. Sirul s=1+2+"a"+1+2 ¸ ıns˘ ¸ ¸va avea valoarea "3a12", primul + fiind operatorul matematic de adunareiar al doilea +, cel de concatenare a ¸irurilor. s1.8 Folosirea argumentelor de la linia de co- mand˘ a1.8.1 Transmiterea argumentelorO aplicatie Java poate primi oricˆte argumente de la linia de comanda ˆ ¸ a ınmomentul lans˘rii ei. Aceste argumente sunt utile pentru a permite utiliza- atorului s˘ specifice diverse optiuni legate de functionarea aplicatiei sau s˘ a ¸ ¸ ¸ afurnizeze anumite date initiale programului. ¸ Atentie ¸ Programele care folosesc argumente de la linia de comand˘ nu sunt 100% apure Java, deoarece unele sisteme de operare, cum ar fi Mac OS, nu au ˆ ınmod normal linie de comand˘.a
  • 33. 32 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN Argumentele de la linia de comand˘ sunt introduse la lansarea unei aplicatii, a ¸fiind specificate dup˘ numele aplicatiei ¸i separate prin spatiu. De exemplu, a ¸ s ¸s˘ presupunem c˘ aplicatia Sortare ordoneaz˘ lexicografic (alfabetic) liniile a a ¸ aunui fi¸ier ¸i prime¸te ca argument de intrare numele fi¸ierului pe care s˘ s s s s aˆ sorteze. Pentru a ordona fi¸ierul "persoane.txt", aplicatia va fi lansat˘ıl s ¸ aastfel: java Sortare persoane.txtA¸adar, formatul general pentru lansarea unei aplicatii care prime¸te argu- s ¸ smente de la linia de comand˘ este: a java NumeAplicatie [arg0 arg1 . . . argn] In cazul ˆ care sunt mai multe, argumentele trebuie separate prin spatii ın ¸iar dac˘ unul dintre argumente contine spatii, atunci el trebuie pus ˆ a ¸ ¸ ıntreghilimele. Evident, o aplicatie poate s˘ nu primeasc˘ nici un argument sau ¸ a apoate s˘ ignore argumentele primite de la linia de comand˘. a a1.8.2 Primirea argumentelorIn momentul lans˘rii unei aplicatii interpretorul parcurge linia de comand˘ cu a ¸ acare a fost lansat˘ aplicattia ¸i, ˆ cazul ˆ care exist˘, transmite programului a ¸ s ın ın aargumentele specificate sub forma unui vector de ¸iruri. Acesta este primit sde aplicatie ca parametru al metodei main. Reamintim c˘ formatul metodei ¸ amain din clasa principal˘ este: a public static void main (String args[])Vectorul args primit ca parametru de metoda main va contine toate argu- ¸mentele transmise programului de la linia de comand˘. aIn cazul apelului java Sortare persoane.txt vectorul args va contine un ¸singur element pe prima s˘ pozitie: args[0]="persoane.txt". a ¸ Vectoru args este instantiat cu un num˘r de elemente egal cu num˘rul ar- ¸ a agumentelor primite de la linia de comand˘. A¸adar, pentru a afla num˘rul de a s aargumente primite de program este suficient s˘ afl˘m dimensiunea vectorului a aargs prin intermediul atributului length:
  • 34. ˘1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA 33 public static void main (String args[]) { int numarArgumente = args.length ; } In cazul ˆ care aplicatia presupune existenta unor argumente de la linia ın ¸ ¸de comand˘, ˆ a acestea nu au fost transmise programului la lansarea sa, vor a ıns˘ap˘rea exceptii (erori) de tipul ArrayIndexOutOfBoundsException. Tratarea a ¸acestor exceptii este prezentat˘ ˆ capitolul ”Exceptii”. ¸ a ın ¸Din acest motiv, este necesar s˘ test˘m dac˘ programul a primit argumentele a a ade la linia de comand˘ necesare pentru functionarea sa ¸i, ˆ caz contrar, s˘ a ¸ s ın aafi¸eze un mesaj de avertizare sau s˘ foloseasc˘ ni¸te valori implicite, ca ˆ s a a s ınexemplul de mai jos:public class Salut { public static void main (String args[]) { if (args.length == 0) { System.out.println("Numar insuficient de argumente!"); System.exit(-1); //termina aplicatia } String nume = args[0]; //exista sigur String prenume; if (args.length >= 1) prenume = args[1]; else prenume = ""; //valoare implicita System.out.println("Salut " + nume + " " + prenume); }} Spre deosebire de limbajul C, vectorul primit de metoda main nu contine¸pe prima pozitie numele aplicatiei, ˆ ¸ ¸ ıntrucˆt ˆ Java numele aplicatiei este a ın ¸chiar numele clasei principale, adic˘ a clasei ˆ care se gase¸te metoda main. a ın s S˘ consider˘ ˆ continuare un exemplu simplu ˆ care se dore¸te afi¸area a a ın ın s spe ecran a argumentelor primite de la linia de comand˘: apublic class Afisare { public static void main (String[] args) { for (int i = 0; i < args.length; i++) System.out.println(args[i]);
  • 35. 34 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN }} Un apel de genul java Afisare Hello Java va produce urm˘torul rezul- atat (aplicatia a primit 2 argumente): ¸ Hello Java Apelul java Afisare "Hello Java" va produce ˆ a alt rezultat (aplicatia ıns˘ ¸a primit un singur argument): Hello Java1.8.3 Argumente numericeArgumentele de la linia de comand˘ sunt primite sub forma unui vector de a¸iruri (obiecte de tip String). In cazul ˆ care unele dintre acestea reprezint˘s ın avalori numerice ele vor trebui convertite din ¸iruri ˆ numere. Acest lucru s ınse realizeaz˘ cu metode de tipul parseTipNumeric aflate ˆ clasa corespun- a ınzatoare tipului ˆ care vrem s˘ facem conversia: Integer, Float, Double, ın aetc. S˘ consider˘m, de exemplu, c˘ aplicatia Power ridic˘ un numar real la o a a a ¸ aputere ˆıntreag˘, argumentele fiind trimise de la linia de comand˘ sub forma: a a java Power "1.5" "2" //ridica 1.5 la puterea 2Conversia celor dou˘ argumente ˆ numere se va face astfel: a ınpublic class Power { public static void main(String args[]) { double numar = Double.parseDouble(args[0]); int putere = Integer.parseInt(args[1]); System.out.println("Rezultat=" + Math.pow(numar, putere)); }} Metodele de tipul parseTipNumeric pot produce exceptii (erori) de tipul ¸NumberFormatException ˆ cazul ˆ care ¸irul primit ca parametru nu reprezint˘ ın ın s aun numar de tipul respectiv. Tratarea acestor exceptii este prezentat˘ ˆ ¸ a ıncapitolul ”Exceptii”. ¸
  • 36. Capitolul 2Obiecte ¸i clase s2.1 Ciclul de viat˘ al unui obiect ¸a2.1.1 Crearea obiectelorIn Java, ca ˆ orice limbaj de programare orientat-obiect, crearea obiectelor ınse realizeaz˘ prin instantierea unei clase ¸i implic˘ urm˘toarele lucruri: a ¸ s a a • Declararea Presupune specificarea tipului acelui obiect, cu alte cuvinte specificarea clasei acestuia (vom vedea c˘ tipul unui obiect poate fi ¸i o interfat˘). a s ¸a NumeClasa numeObiect; • Instantierea ¸ Se realizeaz˘ prin intermediul operatorului new ¸i are ca efect crearea a s efectiv˘ a obiectului cu alocarea spatiului de memorie corespunz˘tor. a ¸ a numeObiect = new NumeClasa(); • Initializarea ¸ Se realizeaz˘ prin intermediul constructorilor clasei respective. Initializarea a ¸ este de fapt parte integrant˘ a procesului de instantiere, ˆ sensul c˘ a ¸ ın a imediat dup˘ alocarea memoriei ca efect al operatorului new este apelat a constructorul specificat. Parantezele rotunde de dup˘ numele clasei in- a dic˘ faptul c˘ acolo este de fapt un apel la unul din constructorii clasei a a ¸i nu simpla specificare a numelui clasei. s Mai general, instantierea ¸i initializarea apar sub forma: ¸ s ¸ 35
  • 37. 36 CAPITOLUL 2. OBIECTE SI CLASE ¸ numeObiect = new NumeClasa([argumente constructor]); S˘ consider˘m urm˘torul exemplu, ˆ care declar˘m ¸i instantiem dou˘ a a a ın a s ¸ aobiecte din clasa Rectangle, clas˘ ce descrie suprafete grafice rectangulare, a ¸definite de coordonatele coltului stˆnga sus (originea) ¸i l˘¸imea, respectiv ¸ a s atˆ altimea.ın˘ ¸ Rectangle r1, r2; r1 = new Rectangle(); r2 = new Rectangle(0, 0, 100, 200); In primul caz Rectangle() este un apel c˘tre constructorul clasei Rectangle acare este responsabil cu initializarea obiectului cu valorile implicite. Dup˘ ¸ acum observ˘m ˆ al doilea caz, initializarea se poate face ¸i cu anumiti para- a ın ¸ s ¸metri, cu conditia s˘ existe un constructor al clasei respective care s˘ accepte ¸ a aparametrii respectivi. Fiecare clas˘ are un set de constructori care se ocup˘ cu initializare a a ¸obiectelor nou create. De exemplu, clasa Rectangle are urm˘torii construc- atori: public Rectangle() public Rectangle(int latime, int inaltime) public Rectangle(int x, int y, int latime, int inaltime) public Rectangle(Point origine) public Rectangle(Point origine, int latime, int inaltime) public Rectangle(Point origine, Dimension dimensiune) Declararea, instantierea ¸i initializarea obiectului pot ap˘rea pe aceea¸i ¸ s ¸ a slinie (cazul cel mai uzual): Rectangle patrat = new Rectangle(0, 0, 100, 100); Obiecte anonime Este posibil˘ ¸i crearea unor obiecte anonime care servesc doar pentru a sinitializarea altor obiecte, caz ˆ care etapa de declarare a referintei obiectului ¸ ın ¸nu mai este prezent˘: a Rectangle patrat = new Rectangle(new Point(0,0), new Dimension(100, 100));
  • 38. ¸˘2.1. CICLUL DE VIATA AL UNUI OBIECT 37 Spatiul de memorie nu este pre-alocat ¸ Declararea unui obiect nu implic˘ sub nici o form˘ alocarea de spatiu a a ¸de memorie pentru acel obiect. Alocarea memoriei se face doar la apeluloperatorului new. Rectangle patrat; patrat.x = 10; //Eroare - lipseste instantierea2.1.2 Folosirea obiectelorOdat˘ un obiect creat, el poate fi folosit ˆ urm˘toarele sensuri: aflarea unor a ın ainformatii despre obiect, schimbarea st˘rii sale sau executarea unor actiuni. ¸ a ¸Aceste lucruri se realizeaza prin aflarea sau schimbarea valorilor variabilelorsale, respectiv prin apelarea metodelor sale. Referirea valorii unei variabile se face prin obiect.variabila De exem-plu clasa Rectangle are variabilele publice x, y, width, height, origin.Aflarea valorilor acestor variabile sau schimbarea lor se face prin constructii ¸de genul: Rectangle patrat = new Rectangle(0, 0, 100, 200); System.out.println(patrat.width); //afiseaza 100 patrat.x = 10; patrat.y = 20; //schimba originea patrat.origin = new Point(10, 20); //schimba originea Accesul la variabilele unui obiect se face ˆ conformitate cu drepturile de ınacces pe care le ofer˘ variabilele respective celorlalte clase. (vezi ”Modifica- atori de acces pentru membrii unei clase”) Apelul unei metode se face prin obiect.metoda([parametri]). Rectangle patrat = new Rectangle(0, 0, 100, 200); patrat.setLocation(10, 20); //schimba originea patrat.setSize(200, 300); //schimba dimensiunea Se observ˘ c˘ valorile variabilelor pot fi modificate indirect prin inter- a amediul metodelor sale. Programarea orientat˘ obiect descurajeaz˘ folosirea a adirect˘ a variabilelor unui obiect deoarece acesta poate fi adus ˆ st˘ri in- a ın aconsistente (ireale). In schimb, pentru fiecare variabil˘ care descrie starea a
  • 39. 38 CAPITOLUL 2. OBIECTE SI CLASE ¸obiectului trebuie s˘ existe metode care s˘ permit˘ schimbarea/aflarea val- a a aorilor variabilelor sale. Acestea se numesc metode de accesare, sau metodesetter - getter ¸i au numele de forma setVariabila, respectiv getVariabila. s patrat.width = -100; //stare inconsistenta patrat.setSize(-100, -200); //metoda setter //metoda setSize poate sa testeze daca noile valori sunt //corecte si sa valideze sau nu schimbarea lor2.1.3 Distrugerea obiectelorMulte limbaje de programare impun ca programatorul s˘ ¸in˘ evidenta obiectelor at a ¸create ¸i s˘ le distrug˘ ˆ mod explicit atunci cˆnd nu mai este nevoie de ele, s a a ın acu alte cuvinte s˘ administreze singur memoria ocupat˘ de obiectele sale. a aPractica a demonstrat c˘ aceast˘ tehnic˘ este una din principalele furnizoare a a ade erori ce duc la functionarea defectuoas˘ a programelor. ¸ a In Java programatorul nu mai are responsabilitatea distrugerii obiectelorsale ˆ ıntrucˆt, ˆ momentul rul˘rii unui program, simultan cu interpretorul a ın aJava, ruleaz˘ ¸i un proces care se ocup˘ cu distrugerea obiectelor care nu a s amai sunt folosite. Acest proces pus la dispozitie de platforma Java de lucru ¸se nume¸te garbage collector (colector de gunoi), prescurtat gc. s Un obiect este eliminat din memorie de procesul de colectare atunci cˆnd anu mai exist˘ nici o referint˘ la acesta. Referintele (care sunt de fapt vari- a ¸a ¸abile) sunt distruse dou˘ moduri: a • natural, atunci cˆnd variabila respectiv˘ iese din domeniul s˘u de viz- a a a ibilitate, de exemplu la terminarea metodei ˆ care ea a fost declarat˘; ın a • explicit, dac˘ atribuim variabilei respective valoare null. a Cum functioneaz˘ colectorul de gunoaie ? ¸ a Colectorul de gunoaie este un proces de prioritate scazut˘ care se exe- acut˘ periodic, scaneaz˘ dinamic memoria ocupat˘ de programul Java aflat a a aˆ executie ¸i marcheaz˘ acele obiecte care au referinte directe sau indirecte.ın ¸ s a ¸Dup˘ ce toate obiectele au fost parcurse, cele care au r˘mas nemarcate sunt a aeliminate automat din memorie.
  • 40. 2.2. CREAREA CLASELOR 39 Apelul metodei gc din clasa System sugereaz˘ ma¸inii virtuale Java s˘ a s a”depun˘ eforturi” ˆ recuperarea memoriei ocupate de obiecte care nu mai a ınsunt folosite, f˘r˘ a forta ˆ a pornirea procesului. aa ¸ ıns˘ Finalizare Inainte ca un obiect s˘ fie eliminat din memorie, procesul gc d˘ acelui a aobiect posibilitatea ”s˘ curete dup˘ el”, apelˆnd metoda de finalizare a obiec- a ¸ a atului respectiv. Uzual, ˆ timpul finaliz˘rii un obiect ˆsi inchide fisierele ¸i ın a ı¸ ssocket-urile folosite, distruge referintele c˘tre alte obiecte (pentru a u¸sura ¸ a ssarcina colectorului de gunoaie), etc. Codul pentru finalizarea unui obiect trebuie scris ˆ ıntr-o metod˘ special˘ a anumita finalize a clasei ce descrie obiectul respectiv. (vezi ”Clasa Object”) Atentie ¸ Nu confundati metoda finalize din Java cu destructorii din C++. Metodafinalize nu are rolul de a distruge obiectul ci este apelat˘ automat ˆ a ınainte deeliminarea obiectului respectiv din memorie.2.2 Crearea claselor2.2.1 Declararea claselorClasele reprezint˘ o modalitate de a introduce noi tipuri de date ˆ a ıntr-oaplicatie Java, cealalt˘ modalitate fiind prin intermediul interfetelor. Declararea ¸ a ¸unei clase respect˘ urm˘torul format general: a a[public][abstract][final]class NumeClasa [extends NumeSuperclasa] [implements Interfata1 [, Interfata2 ... ]] { // Corpul clasei } A¸adar, prima parte a declaratiei o ocup˘ modificatorii clasei. Ace¸tia s ¸ a ssunt:
  • 41. 40 CAPITOLUL 2. OBIECTE SI CLASE ¸ • public Implicit, o clas˘ poate fi folosit˘ doar de clasele aflate ˆ acela¸i pa- a a ın s chet(libr˘rie) cu clasa respectiv˘ (dac˘ nu se specific˘ un anume pa- a a a a chet, toate clasele din directorul curent sunt considerate a fi ˆ acela¸i ın s pachet). O clas˘ declarat˘ cu public poate fi folosit˘ din orice alt˘ a a a a clas˘, indiferent de pachetul ˆ care se g˘se¸te. a ın a s • abstract Declar˘ o clas˘ abstract˘ (¸ablon). O clas˘ abstract˘ nu poate fi a a a s a a instantiat˘, fiind folosit˘ doar pentru a crea un model comun pentru o ¸ a a serie de subclase. (vezi ”Clase ¸i metode abstracte”) s • final Declar˘ c˘ respectiva clas˘ nu poate avea subclase. Declarare claselor a a a finale are dou˘ scopuri: a – securitate: unele metode pot a¸tepta ca parametru un obiect al s unei anumite clase ¸i nu al unei subclase, dar tipul exact al unui s obiect nu poate fi aflat cu exactitate decat ˆ momentul executiei; ın ˆ felul acesta nu s-ar mai putea realiza obiectivul limbajului Java ın ca un program care a trecut compilarea s˘ nu mai fie susceptibil a de nici o eroare. – programare ˆn spririt orientat-obiect: O clasa ”perfect˘” nu tre- ı a buie s˘ mai aib˘ subclase. a a Dup˘ numele clasei putem specifica, dac˘ este cazul, faptul c˘ respectiva a a aclas˘ este subclas˘ a unei alte clase cu numele NumeSuperclasa sau/¸i c˘ a a s aimplementeaz˘ una sau mai multe interfete, ale c˘ror nume trebuie separate a ¸ aprin virgul˘. a2.2.2 Extinderea claselorSpre deosebire de alte limbaje de programare orientate-obiect, Java permitedoar mo¸tenirea simpl˘, ceea ce ˆ s a ıneamn˘ c˘ o clas˘ poate avea un singur a a ap˘rinte (superclas˘). Evident, o clas˘ poate avea oricˆti mo¸tenitori (sub- a a a a sclase), de unde rezult˘ c˘ multimea tuturor claselor definite ˆ Java poate fi a a ¸ ınvazut˘ ca un arbore, r˘d˘cina acestuia fiind clasa Object. A¸adar, Object a a a seste singura clas˘ care nu are p˘rinte, fiind foarte important˘ ˆ modul de a a a ınlucru cu obiecte si structuri de date ˆ Java. ın
  • 42. 2.2. CREAREA CLASELOR 41 Extinderea unei clase se realizeaz˘ folosind cuvˆntul cheie extends: a a class B extends A {...} // A este superclasa clasei B // B este o subclasa a clasei A O subclas˘ mo¸tene¸te de la p˘rintele s˘u toate variabilele ¸i metodele a s s a a scare nu sunt private.2.2.3 Corpul unei claseCorpul unei clase urmeaz˘ imediat dup˘ declararea clasei ¸i este cuprins ˆ a a s ıntreacolade. Continutul acestuia este format din: ¸ • Declararea ¸i, eventual, initializarea variabilelor de instant˘ ¸i de clas˘ s ¸ ¸a s a (cunoscute ˆ ımpreun˘ ca variabile membre). a • Declararea ¸i implementarea constructorilor. s • Declararea ¸i implementarea metodelor de instanta ¸i de clas˘ (cunos- s ¸ s a cute ˆ ımpreun˘ ca metode membre). a • Declararea unor clase imbricate (interne). Spre deosebire de C++, nu este permis˘ doar declararea metodei ˆ corpul a ınclasei, urmˆnd ca implementare s˘ fie facut˘ ˆ afara ei. Implementarea a a a ınmetodelor unei clase trebuie s˘ se fac˘ obligatoriu ˆ corpul clasei. a a ın// C++class A { void metoda1(); int metoda2() { // Implementare }}A::metoda1() { // Implementare}
  • 43. 42 CAPITOLUL 2. OBIECTE SI CLASE ¸// Javaclass A { void metoda1(){ // Implementare } void metoda2(){ // Implementare }} Variabilele unei clase pot avea acela¸i nume cu metodele clasei, care poate sfi chiar numele clasei, f˘r˘ a exista posibilitatea aparitiei vreunei ambiguit˘¸i aa ¸ atdin punctul de vedere al compilatorului. Acest lucru este ˆ a total nere- ıns˘comandat dac˘ ne gˆndim din perspectiva lizibilit˘¸ii (clarit˘¸ii) codului, a a at atdovedind un stil ineficient de progamare.class A { int A; void A() {}; // Corect pentru compilator // Nerecomandat ca stil de programare} Atentie ¸ Variabilele ¸i metodele nu pot avea ca nume un cuvˆnt cheie Java. s a2.2.4 Constructorii unei claseConstructorii unei clase sunt metode speciale care au acela¸i nume cu cel sal clasei, nu returneaz˘ nici o valoare ¸i sunt folositi pentru initializarea a s ¸ ¸obiectelor acelei clase ˆ momentul instantierii lor. ın ¸class NumeClasa { [modificatori] NumeClasa([argumente]) { // Constructor
  • 44. 2.2. CREAREA CLASELOR 43 }} O clas˘ poate avea unul sau mai multi constructori care trebuie ˆ a s˘ a ¸ ıns˘ adifere prin lista de argumente primite. In felul acesta sunt permise diversetipuri de initializ˘ri ale obiectelor la crearea lor, ˆ functie de num˘rul para- ¸ a ın ¸ ametrilor cu care este apelat constructorul. S˘ consider˘m ca exemplu declararea unei clase care descrie notiunea de a a ¸dreptunghi ¸i trei posibili constructori pentru aceasta clas˘. s aclass Dreptunghi { double x, y, w, h; Dreptunghi(double x1, double y1, double w1, double h1) { // Cel mai general constructor x=x1; y=y1; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi(double w1, double h1) { // Constructor cu doua argumente x=0; y=0; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi() { // Constructor fara argumente x=0; y=0; w=0; h=0; System.out.println("Instantiere dreptunghi"); }} Constructorii sunt apelati automat la instantierea unui obiect. In cazul ¸ ¸ˆ care dorim s˘ apel˘m explicit constructorul unei clase folosim expresiaın a a this( argumente ),care apeleaz˘ constructorul corespunz˘tor (ca argumente) al clasei respec- a ative. Aceast˘ metod˘ este folosit˘ atunci cˆnd sunt implementati mai multi a a a a ¸ ¸constructori pentru o clas˘, pentru a nu repeta secventele de cod scrise deja a ¸la constructorii cu mai multe argumente (mai generali). Mai eficient, f˘r˘ aa
  • 45. 44 CAPITOLUL 2. OBIECTE SI CLASE ¸a repeta acelea¸i secvente de cod ˆ toti constructorii (cum ar fi afi¸area s ¸ ın ¸ smesajului ”Instantiere dreptunghi”), clasa de mai sus poate fi rescris˘ astfel: aclass Dreptunghi { double x, y, w, h; Dreptunghi(double x1, double y1, double w1, double h1) { // Implementam doar constructorul cel mai general x=x1; y=y1; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi(double w1, double h1) { this(0, 0, w1, h1); // Apelam constructorul cu 4 argumente } Dreptunghi() { this(0, 0); // Apelam constructorul cu 2 argumente }} Dintr-o subclas˘ putem apela explicit constructorii superclasei cu expresia a super( argumente ). S˘ presupunem c˘ dorim s˘ cre˘m clasa Patrat, derivat˘ din clasa Dreptunghi: a a a a aclass Patrat extends Dreptunghi { Patrat(double x, double y, double d) { super(x, y, d, d); // Apelam constructorul superclasei }} Atentie ¸ Apelul explcit al unui constructor nu poate ap˘rea decˆt ˆ a a ıntr-un alt con-structor si trebuie s˘ fie prima instructiune din constructorul respectiv. a ¸
  • 46. 2.2. CREAREA CLASELOR 45 Constructorul implicit Constructorii sunt apelati automat la instantierea unui obiect. In cazul ¸ ¸ˆ care scriem o clas˘ care nu are declarat nici un constructor, sistemul ˆın a ıicreeaz˘ automat un constructor implicit, care nu prime¸te nici un argument a s¸i care nu face nimic. Deci prezenta constructorilor ˆ corpul unei clase nus ¸ ıneste obligatorie. Dac˘ ˆ a scriem un constructor pentru o clas˘, care are mai a ıns˘ amult de un argument, atunci constructorul implicit (f˘r˘ nici un argument) aanu va mai fi furnizat implicit de c˘tre sistem. S˘ consider˘m, ca exemplu, a a aurm˘toarele declaratii de clase: a ¸ class Dreptunghi { double x, y, w, h; // Nici un constructor } class Cerc { double x, y, r; // Constructor cu 3 argumente Cerc(double x, double y, double r) { ... }; } S˘ consider˘m acum dou˘ instantieri ale claselor de mai sus: a a a ¸ Dreptunghi d = new Dreptunghi(); // Corect (a fost generat constructorul implicit) Cerc c; c = new Cerc(); // Eroare la compilare ! c = new Cerc(0, 0, 100); // Varianta corecta In cazul mo¸tenirii unei clase, instantierea unui obiect din clasa extins˘ s ¸ aimplic˘ instantierea unui obiect din clasa p˘rinte. Din acest motiv, fiecare a ¸ aconstructor al clasei fiu va trebui s˘ aib˘ un constructor cu aceea¸i signatur˘ a a s aˆ p˘rinte sau s˘ apeleze explicit un constructor al clasei extinse folosindın a aexpresia super([argumente]), ˆ caz contrar fiind semnalat˘ o eroare la ın acompilare.
  • 47. 46 CAPITOLUL 2. OBIECTE SI CLASE ¸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;}} Constructorii unei clase pot avea urm˘torii modificatori de acces: apublic, protected, private ¸i cel implicit. s • public In orice alt˘ clas˘ se pot crea instante ale clasei respective. a a ¸ • protected Doar ˆ subclase pot fi create obiecte de tipul clasei respective. ın • private In nici o alt˘ clas˘ nu se pot instantia obiecte ale acestei clase. O ast- a a ¸ fel de clas˘ poate contine metode publice (numite ”factory methods”) a ¸ care s˘ fie responsabile cu crearea obiectelor, controlˆnd ˆ felul acesta a a ın diverse aspecte legate de instantierea clasei respective. ¸ • implicit Doar ˆ clasele din acela¸i pachet se pot crea instante ale clasei respec- ın s ¸ tive.2.2.5 Declararea variabilelorVariabilele membre ale unei clase se declar˘ de obicei ˆ a ınaintea metodelor,de¸i acest lucru nu este impus de c˘tre compilator. s a
  • 48. 2.2. CREAREA CLASELOR 47 class NumeClasa { // Declararea variabilelor // Declararea metodelor} Variabilele membre ale unei clase se declar˘ ˆ corpul clasei ¸i nu ˆ corpul a ın s ınunei metode, fiind vizibile ˆ toate metodele respectivei clase. Variabilele ındeclarate ˆ cadrul unei metode sunt locale metodei respective. ın Declararea unei variabile presupune specificarea urm˘toarelor lucruri: a • numele variabilei • tipul de date al acesteia • nivelul de acces la acea variabila din alte clase • dac˘ este constant˘ sau nu a a • dac˘ este variabil˘ de instant˘ sau de clas˘ a a ¸a a • alti modificatori ¸ Generic, o variabil˘ se declar˘ astfel: a a [modificatori] Tip numeVariabila [ = valoareInitiala ];unde un modificator poate fi : • un modificator de acces : public, protected, private (vezi ”Mod- ificatori de acces pentru membrii unei clase”) • unul din cuvintele rezervate: static, final, transient, volatile Exemple de declaratii de variabile membre: ¸class Exemplu { double x; protected static int n; public String s = "abcd"; private Point p = new Point(10, 10); final static long MAX = 100000L;}
  • 49. 48 CAPITOLUL 2. OBIECTE SI CLASE ¸ S˘ analiz˘m modificatorii care pot fi specificati pentru o variabil˘, altii a a ¸ a ¸decˆt cei de acces care sunt tratati ˆ a ıntr-o sectiune separata: ”Specificatori ¸de acces pentru membrii unei clase”. • static Prezenta lui declar˘ c˘ o variabil˘ este variabil˘ de clas˘ ¸i nu de ¸ a a a a a s instant˘. (vezi ”Membri de instanta ¸i membri de clas˘”) ¸a ¸ s a int variabilaInstanta ; static int variabilaClasa; • final Indic˘ faptul c˘ valoarea variabilei nu mai poate fi schimbat˘, cu alte a a a cuvinte este folosit pentru declararea constantelor. final double PI = 3.14 ; ... PI = 3.141; // Eroare la compilare ! Prin conventie, numele variabilelor finale se scriu cu litere mari. Folosirea ¸ lui final aduce o flexibilitate sporit˘ ˆ lucrul cu constante, ˆ sensul a ın ın c˘ valoarea unei variabile nu trebuie specificat˘ neap˘rat la declararea a a a ei (ca ˆ exemplul de mai sus), ci poate fi specificat˘ ¸i ulterior ˆ ın as ıntr-un constructor, dup˘ care ea nu va mai putea fi modificat˘. a a class Test { final int MAX; Test() { MAX = 100; // Corect MAX = 200; // Eroare la compilare ! } } • transient Este folosit la serializarea obiectelor, pentru a specifica ce variabile membre ale unui obiect nu particip˘ la serializare. (vezi ”Serializarea a obiectelor”)
  • 50. 2.2. CREAREA CLASELOR 49 • volatile Este folosit pentru a semnala compilatorului s˘ nu execute anumite a optimiz˘ri asupra membrilor unei clase. Este o facilitate avansat˘ a a a limbajului Java.2.2.6 this ¸i super sSunt variabile predefinite care fac referinta, ˆ cadrul unui obiect, la obiectul ¸ ınpropriu-zis (this), respectiv la instanta p˘rintelui (super). Sunt folosite ¸ aˆ general pentru a rezolva conflicte de nume prin referirea explicit˘ a uneiın avariabile sau metode membre. Dup˘ cum am v˘zut, utilizate sub form˘ de a a ametode au rolul de a apela constructorii corespunz˘tori ca argumente ai clasei acurente, respectiv ai superclaseiclass 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();
  • 51. 50 CAPITOLUL 2. OBIECTE SI CLASE ¸ System.out.println(x); }}2.3 Implementarea metodelor2.3.1 Declararea metodelorMetodele sunt responsabile cu descrierea comportamentului unui obiect. In-trucˆt Java este un limbaj de programare complet orientat-obiect, metodele ase pot g˘si doar ˆ cadrul claselor. Generic, o metod˘ se declar˘ astfel: a ın a a [modificatori] TipReturnat numeMetoda ( [argumente] ) [throws TipExceptie1, TipExceptie2, ...] { // Corpul metodei } unde un modificator poate fi : • un specificator de acces : public, protected, private (vezi ”Spec- ificatori de acces pentru membrii unei clase”) • unul din cuvintele rezervate: static, abstract, final, native, synchronized S˘ analiz˘m modificatorii care pot fi specificati pentru o metod˘, altii a a ¸ a ¸decˆt cei de acces care sunt tratati ˆ a ¸ ıntr-o sectiune separat˘. ¸ a • static Prezenta lui declar˘ c˘ o metod˘ este de clas˘ ¸i nu de instant˘. (vezi ¸ a a a as ¸a ”Membri de instanta ¸i membri de clas˘”) ¸ s a void metodaInstanta(); static void metodaClasa(); • abstract Permite declararea metodelor abstracte. O metod˘ abstract˘ este o a a metod˘ care nu are implementare ¸i trebuie obligatoriu s˘ fac˘ parte a s a a dintr-o clas˘ abstract˘. (vezi ”Clase ¸i metode abstracte”) a a s
  • 52. 2.3. IMPLEMENTAREA METODELOR 51 • final Specific˘ faptul c˘ acea metoda nu mai poate fi supradefinit˘ ˆ sub- a a a ın clasele clasei ˆ care ea este definit˘ ca fiind final˘. Acest lucru este ın a a util dac˘ respectiva metod˘ are o implementare care nu trebuie schim- a a bat˘ sub nici o form˘ ˆ subclasele ei, fiind critic˘ pentru consistenta a a ın a ¸ st˘rii unui obiect. De exemplu, studentilor unei universit˘¸i trebuie s˘ a ¸ at a li se calculeze media finala, ˆ functie de notele obtinute la examene, ın ¸ ¸ ˆ aceea¸i manier˘, indiferent de facultatea la care sunt. ın s a class Student { ... final float calcMedie(float note[], float ponderi[]) { ... } ... } class StudentInformatica extends Student { float calcMedie(float note[], float ponderi[]) { return 10.00; } }// Eroare la compilare ! • native In cazul ˆ care avem o libr˘rie important˘ de functii scrise ˆ alt limbaj ın a a ¸ ın de programare, cum ar fi C, C++ ¸i limbajul de asamblare, acestea s pot fi refolosite din programele Java. Tehnologia care permite acest lucru se nume¸te JNI (Java Native Interface) ¸i permite asocierea dintre s s metode Java declarate cu native ¸i metode native scrise ˆ limbajele s ın de programare mentionate. ¸ • synchronized Este folosit ˆ cazul ˆ care se lucreaz˘ cu mai multe fire de executie iar ın ın a ¸ metoda respectiv˘ gestioneaz˘ resurse comune. Are ca efect construirea a a unui monitor care nu permite executarea metodei, la un moment dat, decˆt unui singur fir de executie. (vezi ”Fire de executie”) a ¸
  • 53. 52 CAPITOLUL 2. OBIECTE SI CLASE ¸2.3.2 Tipul returnat de o metod˘ aMetodele pot sau nu s˘ returneze o valoare la terminarea lor. Tipul returnat apoate fi atˆt un tip primitiv de date sau o referint˘ la un obiect al unei clase. a ¸aIn cazul ˆ care o metod˘ nu returneaz˘ nimic atunci trebuie obligatoriu ın a aspecificat cuvˆntul cheie void ca tip returnat: a public void afisareRezultat() { System.out.println("rezultat"); } private void deseneaza(Shape s) { ... return; } Dac˘ o metod˘ trebuie s˘ returneze o valoare acest lucru se realizeaz˘ prin a a a aintermediul instructiunii return, care trebuie s˘ apar˘ ˆ toate situatiile de ¸ a a ın ¸terminare a functiei. ¸ double radical(double x) { if (x >= 0) return Math.sqrt(x); else { System.out.println("Argument negativ !"); // Eroare la compilare // Lipseste return pe aceasta ramura } } In cazul ˆ care ˆ declaratia functiei tipul returnat este un tip primitiv de ın ın ¸ ¸date, valoarea returnat˘ la terminarea functiei trebuie s˘ aib˘ obligatoriu acel a ¸ a atip sau un subtip al s˘u, altfel va fi furnizat˘ o eroare la compilare. In general, a aorice atribuire care implic˘ pierderi de date este tratat˘ de compilator ca a aeroare. int metoda() { return 1.2; // Eroare } int metoda() {
  • 54. 2.3. IMPLEMENTAREA METODELOR 53 return (int)1.2; // Corect } double metoda() { return (float)1; // Corect } Dac˘ valoarea returnat˘ este o referint˘ la un obiect al unei clase, atunci a a ¸aclasa obiectului returnat trebuie s˘ coincid˘ sau s˘ fie o subclas˘ a clasei a a a aspecificate la declararea metodei. De exemplu, fie clasa Poligon ¸i subclasa sacesteia 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˘tre o metod˘ a aSignatura unei metode este dat˘ de numarul ¸i tipul argumentelor primite a sde acea metod˘. Tipul de date al unui argument poate fi orice tip valid al alimbajului Java, atˆt tip primitiv cˆt ¸i tip referint˘. a a s ¸a TipReturnat metoda([Tip1 arg1, Tip2 arg2, ...]) Exemplu:
  • 55. 54 CAPITOLUL 2. OBIECTE SI CLASE ¸ void adaugarePersoana(String nume, int varsta, float salariu) // String este tip referinta // int si float sunt tipuri primitive Spre deosebire de alte limbaje, ˆ Java nu pot fi trimise ca parametri ai ınunei metode referinte la alte metode (functii), ˆ a pot fi trimise referinte la ¸ ¸ ıns˘ ¸obiecte care s˘ contin˘ implementarea acelor metode, pentru a fi apelate. a ¸ aPˆna la aparitia versiunii 1.5, ˆ Java o metod˘ nu putea primi un num˘r a ¸ ın a avariabil de argumente, ceea ce ˆ ınseamna c˘ apelul unei metode trebuia s˘ se a afac˘ cu specificarea exact˘ a numarului ¸i tipurilor argumentelor. Vom anal- a a siza ˆıntr-o sectiune separat˘ modalitate de specificare a unui num˘r variabil ¸ a ade argumente pentru o metod˘. aNumele argumentelor primite trebuie s˘ difere ˆ a ıntre ele ¸i nu trebuie s˘ co- s aincid˘ cu numele nici uneia din variabilele locale ale metodei. Pot ˆ a s˘ a ıns˘ acoincid˘ cu numele variabilelor membre ale clasei, caz ˆ care diferentierea a ın ¸dintre ele se va face prin intermediul variabile this.class Cerc { int x, y, raza; public Cerc(int x, int y, int raza) { this.x = x; this.y = y; this.raza = raza; }} In Java argumentele sunt trimise doar prin valoare (pass-by-value).Acest lucru ˆınseamn˘ c˘ metoda receptioneaz˘ doar valorile variabilelor prim- a a ¸ aite ca parametri.Cˆnd argumentul are tip primitiv de date, metoda nu-i poate schimba val- aoarea decˆt local (ˆ cadrul metodei); la revenirea din metod˘ variabila are a ın aaceea¸i valoare ca ˆ s ınaintea apelului, modific˘rile f˘cute ˆ cadrul metodei fi- a a ınind pierdute.Cˆnd argumentul este de tip referint˘, metoda nu poate schimba valoarea a ¸areferintei obiectului, ˆ a poate apela metodele acelui obiect ¸i poate modifica ¸ ıns˘ sorice variabil˘ membr˘ accesibil˘. a a a A¸adar, dac˘ dorim ca o metod˘ s˘ schimbe starea (valoarea) unui argu- s a a ament primit, atunci el trebuie s˘ fie neaparat de tip referint˘. a ¸a
  • 56. 2.3. IMPLEMENTAREA METODELOR 55De exemplu, s˘ consider˘m clasa Cerc descris˘ anterior ˆ care dorim s˘ a a a ın aimplement˘m o metod˘ care s˘ returneze parametrii cercului. a a a// Varianta incorecta:class Cerc { private int x, y, raza; public void aflaParametri(int valx, int valy, int valr) { // Metoda nu are efectul dorit! valx = x; valy = y; valr = raza; }} Aceast˘ metod˘ nu va realiza lucrul propus ˆ a a ıntrucˆt ea prime¸te doar a svalorile variabilelor valx, valy ¸i valr ¸i nu referinte la ele (adresele lor s s ¸de memorie), astfel ˆ at s˘ le poat˘ modifica valorile. In concluzie, metoda ıncˆ a anu realizeaz˘ nimic pentru c˘ nu poate schimba valorile variabilelor primite a aca argumente. Pentru a rezolva lucrul propus trebuie s˘ definim o clas˘ suplimentar˘ a a acare s˘ descrie parametrii pe care dorim s˘-i afl˘m: a a a// Varianta corectaclass Param { public int x, y, raza;}class Cerc { private int x, y, raza; public void aflaParametri(Param param) { param.x = x; param.y = y; param.raza = raza; }} Argumentul param are tip referint˘ ¸i, de¸i nu ˆ schimb˘m valoarea (val- ¸a s s ıi aoarea sa este adresa de memorie la care se gase¸te ¸i nu poate fi schimbat˘), s s a
  • 57. 56 CAPITOLUL 2. OBIECTE SI CLASE ¸putem schimba starea obiectului, adic˘ informatia propriu-zis˘ continut˘ de a ¸ a ¸ aacesta. Varianta de mai sus a fost dat˘ pentru a clarifica modul de trimitere a aargumentelor unei metode. Pentru a afla ˆ a valorile variabilelor care descriu ıns˘starea unui obiect se folosesc metode de tip getter ˆ ¸ite de metode setter ınsotcare s˘ permit˘ schimbarea st˘rii obiectului: a a aclass 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˘r variabil de argumente aIncepˆnd cu versiunea 1.5 a limbajului Java, exist˘ posibilitate de a declara a ametode care s˘ primeasc˘ un num˘r variabil de argumente. Noutatea const˘ a a a aˆ folosirea simbolului ..., sintaxa unei astfel de metode fiind:ın [modificatori] TipReturnat metoda(TipArgumente ... args)args reprezint˘ un vector avˆnd tipul specificat ¸i instantiat cu un num˘r a a s ¸ avariabil de argumente, ˆ functie de apelul metodei. Tipul argumentelor ın ¸poate fi referint˘ sau primitiv. Metoda de mai jos afi¸eaz˘ argumentele prim- ¸a s aite, care pot fi 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);
  • 58. 2.3. IMPLEMENTAREA METODELOR 572.3.5 Supraˆ arcarea ¸i supradefinirea metodelor ınc˘ sSupraˆ arcarea ¸i supradefinirea metodelor sunt dou˘ concepte extrem de ınc˘ s autile ale program˘rii orientate obiect, cunoscute ¸i sub denumirea de polimor- a sfism, ¸i se refer˘ la: s a • supraˆncarcarea (overloading) : ˆ cadrul unei clase pot exista metode ı ın cu acela¸i nume cu conditia ca signaturile lor s˘ fie diferite (lista de s ¸ a argumente primite s˘ difere fie prin num˘rul argumentelor, fie prin a a tipul lor) astfel ˆ at la apelul functiei cu acel nume s˘ se poat˘ stabili ıncˆ ¸ a a ˆ mod unic care dintre ele se execut˘. ın a • supradefinirea (overriding): o subclas˘ poate rescrie o metod˘ a cla- a a sei p˘rinte prin implementarea unei metode cu acela¸i nume ¸i aceea¸i a s s s signatur˘ ca ale superclasei. aclass 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˘ supradefinit˘ poate s˘: a a a • ignore complet codul metodei corespunz˘toare din superclas˘ (cazul a a de mai sus): B b = new B(); b.metoda(); // Afiseaza "B: metoda fara parametru"
  • 59. 58 CAPITOLUL 2. OBIECTE SI CLASE ¸ • extind˘ codul metodei p˘rinte, executˆnd ˆ a a a ınainte de codul propriu ¸i s functia p˘rintelui: ¸ a class B extends A { // Supradefinire prin extensie void metoda() { super.metoda(); System.out.println("B: metoda fara parametru"); } } . . . B b = new B(); b.metoda(); /* Afiseaza ambele mesaje: "A: metoda fara parametru" "B: metoda fara parametru" */ O metod˘ nu poate supradefini o metod˘ declarat˘ final˘ ˆ clasa p˘rinte. a a a a ın a Orice clas˘ care nu este abstract˘ trebuie obligatoriu s˘ supradefineasc˘ a a a ametodele abstracte ale superclasei (dac˘ este cazul). In cazul ˆ care o clas˘ a ın anu supradefine¸te toate metodele abstracte ale p˘rintelui, ea ˆ a¸i este ab- s a ıns˘sstract˘ ¸i va trebui declarat˘ ca atare. as a In Java nu este posibil˘ supraˆ arcarea operatorilor. a ınc˘2.4 Modificatori de accesModificatorii de acces sunt cuvinte rezervate ce controleaz˘ accesul celor- alate clase la membrii unei clase. Specificatorii de acces pentru variabilele¸i metodele unei clase sunt: public, protected, private ¸i cel implicit (las snivel de pachet), iar nivelul lor de acces este dat ˆ tabelul de mai jos: ın Specificator Clasa Sublasa Pachet Oriunde private X protected X X* X public X X X X implicit X X
  • 60. ¸˘ ¸ ˘2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA 59 A¸adar, dac˘ nu este specificat nici un modificator de acces, implicit s anivelul de acces este la nivelul pachetului. In cazul ˆ care declar˘m un ın amembru ”protected” atunci accesul la acel membru este permis din subclaseleclasei ˆ care a fost declarat dar depinde ¸i de pachetul ˆ care se gase¸te ın s ın ssubclasa: dac˘ sunt ˆ acela¸i pachet accesul este permis, dac˘ nu sunt ˆ a ın s a ınacela¸i pachet accesul nu este permis decˆt pentru obiecte de tipul subclasei. s a Exemple de declaratii: ¸ private int secretPersonal; protected String secretDeFamilie; public Vector pentruToti; long doarIntrePrieteni; private void metodaInterna(); public String informatii();2.5 Membri de instant˘ ¸i membri de clas˘ ¸a s aO clas˘ Java poate contine dou˘ tipuri de variabile ¸i metode : a ¸ a s • de instant˘: declarate f˘r˘ modificatorul static, specifice fiec˘rei ¸a a a a instante create dintr-o clas˘ ¸i ¸ as • de clas˘: declarate cu modificatorul static, specifice clasei. a2.5.1 Variabile de instant˘ ¸i de clas˘ ¸a s aCˆnd declar˘m o variabil˘ membr˘ f˘r˘ modificatorul static, cum ar fi x ˆ a a a a aa ınexemplul de mai jos:class Exemplu { int x ; //variabila de instanta}se declar˘ de fapt o variabil˘ de instant˘, ceea ce ˆ a a ¸a ınseamn˘ c˘ la fiecare creare a aa unui obiect al clasei Exemplu sistemul aloc˘ o zon˘ de memorie separat˘ a a apentru memorarea valorii lui x. Exemplu o1 = new Exemplu(); o1.x = 100;
  • 61. 60 CAPITOLUL 2. OBIECTE SI CLASE ¸ Exemplu o2 = new Exemplu(); o2.x = 200; System.out.println(o1.x); // Afiseaza 100 System.out.println(o2.x); // Afiseaza 200 A¸adar, fiecare obiect nou creat va putea memora valori diferite pentru svariabilele sale de instant˘. ¸a Pentru variabilele de clas˘ (statice) sistemul aloc˘ o singur˘ zon˘ de mem- a a a aorie la care au acces toate instantele clasei respective, ceea ce ˆ ¸ ınseamn˘ c˘ a adac˘ un obiect modific˘ valoarea unei variabile statice ea se va modifica ¸i a a spentru toate celelalte obiecte. Deoarece nu depind de o anumit˘ instanta a a ¸˘unei clase, variabilele statice pot fi referite ¸i sub forma: s NumeClasa.numeVariabilaStaticaclass Exemplu { int x ; // Variabila de instanta static long n; // Variabila de clasa} . . . Exemplu o1 = new Exemplu(); Exemplu o2 = new Exemplu(); o1.n = 100; System.out.println(o2.n); // Afiseaza 100 o2.n = 200; System.out.println(o1.n); // Afiseaza 200 System.out.println(Exemplu.n); // Afiseaza 200 // o1.n, o2.n si Exemplu.n sunt referinte la aceeasi valoare Initializarea variabilelor de clas˘ se face o singur˘ dat˘, la ˆ arcarea ˆ ¸ a a a ınc˘ ınmemorie a clasei respective, ¸i este realizat˘ prin atribuiri obi¸nuite: s a sclass Exemplu { static final double PI = 3.14; static long nrInstante = 0; static Point p = new Point(0,0);}
  • 62. ¸˘ ¸ ˘2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA 612.5.2 Metode de instant˘ ¸i de clas˘ ¸a s aSimilar ca la variabile, metodele declarate f˘r˘ modificatorul static sunt aametode de instant˘ iar cele declarate cu static sunt metode de clas˘ (stat- ¸a aice). Diferenta ˆ ¸ ıntre cele dou˘ tipuri de metode este urm˘toarea: a a • metodele de instant˘ opereaz˘ atˆt pe variabilele de instant˘ cˆt ¸i pe ¸a a a ¸a a s cele statice ale clasei; • metodele de clas˘ opereaz˘ doar pe variabilele statice ale clasei. a a class Exemplu { int x ; // Variabila de instanta static long n; // Variabila de clasa void metodaDeInstanta() { n ++; // Corect x --; // Corect } static void metodaStatica() { n ++; // Corect x --; // Eroare la compilare ! } } Intocmai ca ¸i la variabilele statice, ˆ s ıntrucˆt metodele de clas˘ nu depind a ade starea obiectelor clasei respective, apelul lor se poate face ¸i sub forma: s NumeClasa.numeMetodaStatica Exemplu.metodaStatica(); // Corect, echivalent cu Exemplu obj = new Exemplu(); obj.metodaStatica(); // Corect, de asemenea Metodele de instant˘ nu pot fi apelate decˆt pentru un obiect al clasei ¸a arespective: Exemplu.metodaDeInstanta(); // Eroare la compilare ! Exemplu obj = new Exemplu(); obj.metodaDeInstanta(); // Corect
  • 63. 62 CAPITOLUL 2. OBIECTE SI CLASE ¸2.5.3 Utilitatea membrilor de clas˘ aMembrii de clas˘ sunt folositi pentru a pune la dispozitie valori ¸i metode a ¸ ¸ sindependente de starea obiectelor dintr-o anumita clas˘. a Declararea eficient˘ a constantelor a S˘ consider˘m situatia cˆnd dorim s˘ declar˘m o constant˘. a a ¸ a a a aclass Exemplu { final double PI = 3.14; // Variabila finala de instanta} La fiecare instantiere a clasei Exemplu va fi rezervat˘ o zon˘ de memorie ¸ a apentru variabilele finale ale obiectului respectiv, ceea ce este o risip˘ ˆ a ıntrucˆt aaceste constante au acelea¸i valori pentru toate instantele clasei. Declararea s ¸corect˘ a constantelor trebuie a¸adar facut˘ cu modificatorii static ¸i final, a s a spentru a le rezerva o singur˘ zon˘ de memorie, comun˘ tuturor obiectelor: a a aclass Exemplu { static final double PI = 3.14; // Variabila finala de clasa} Num˘rarea obiectelor unei clase a Num˘rarea obiectelor unei clase poate fi f˘cut˘ extrem de simplu folosind a a ao variabil˘ static˘ ¸i este util˘ ˆ situatiile cˆnd trebuie s˘ control˘m diver¸i a as a ın ¸ a a a sparametri legati de crearea obiectelor unei clase. ¸class Exemplu { static long nrInstante = 0; Exemplu() { // Constructorul este apelat la fiecare instantiere nrInstante ++; }}
  • 64. ¸˘ ¸ ˘2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA 63 Implementarea functiilor globale ¸ Spre deosebire de limbajele de programare procedurale, ˆ Java nu putem ınavea functii globale definite ca atare, ˆ ¸ ıntrucˆt ”orice este un obiect”. Din aacest motiv chiar ¸i metodele care au o functionalitate global˘ trebuie im- s ¸ aplementate ˆ cadrul unor clase. Acest lucru se va face prin intermediul ınmetodelor de clas˘ (globale), deoarece acestea nu depind de starea partic- aular˘ a obiectelor din clasa respectiv˘. De exemplu, s˘ consider˘m functia a a a a ¸sqrt care extrage radicalul unui num˘r ¸i care se g˘se¸te ˆ clasa Math. Dac˘ a s a s ın anu ar fi fost functie de clas˘, apelul ei ar fi trebuit f˘cut astfel (incorect, de ¸ a aaltfel): // Incorect ! Math obj = new Math(); double rad = obj.sqrt(121);ceea ce ar fi fost extrem de nepl˘cut... Fiind ˆ a metod˘ static˘ ea poate fi a ıns˘ a aapelat˘ prin: Math.sqrt(121) . aA¸adar, functiile globale necesare unei aplicatii vor fi grupate corespunz˘tor s ¸ ¸ aˆ diverse clase ¸i implementate ca metode statice.ın s2.5.4 Blocuri statice de initializare ¸Variabilele statice ale unei clase sunt initializate la un moment care precede ¸prima utilizare activ˘ a clasei respective. Momentul efectiv depinde de im- aplementarea ma¸inii virtuale Java ¸i poart˘ numele de initializarea clasei. Pe s s a ¸lˆng˘ setarea valorilor variabilelor statice, ˆ aceast˘ etap˘ sunt executate ¸i a a ın a a sblocurile statice de initializare ale clasei. Acestea sunt secvente de cod de ¸ ¸forma: static { // Bloc static de initializare; ... }care se comport˘ ca o metod˘ static˘ apelat˘ automat de c˘tre ma¸ina vir- a a a a a stual˘. Variabilele referite ˆ a ıntr-un bloc static de initializare trebuie s˘ fie ¸ aobligatoriu de clas˘ sau locale blocului: apublic class Test { // Declaratii de variabile statice
  • 65. 64 CAPITOLUL 2. OBIECTE SI CLASE ¸ static int x = 0, y, z; // 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 Clase imbricate2.6.1 Definirea claselor imbricateO clas˘ imbricat˘ este, prin definitie, o clas˘ membr˘ a unei alte clase, numit˘ a a ¸ a a a¸i clas˘ de acoperire. In functie de situatie, definirea unei clase interne ses a ¸ ¸poate face fie ca membru al clasei de acoperire - caz ˆ care este accesibil˘ ın atuturor metodelor, fie local ˆ cadrul unei metode. ınclass ClasaDeAcoperire{ class ClasaImbricata1 { // Clasa membru } void metoda() { class ClasaImbricata2 { // Clasa locala metodei } }
  • 66. 2.6. CLASE IMBRICATE 65} Folosirea claselor imbricate se face atunci cˆnd o clas˘ are nevoie ˆ im- a a ınplementarea ei de o alt˘ clas˘ ¸i nu exist˘ nici un motiv pentru care aceasta a as adin urm˘ s˘ fie declarat˘ de sine st˘t˘toare (nu mai este folosit˘ nic˘ieri). a a a aa a a O clas˘ imbricat˘ are un privilegiu special fat˘ de celelalte clase ¸i anume a a ¸a sacces nerestrictionat la toate variabilele clasei de acoperire, chiar dac˘ aces- ¸ atea sunt private. O clas˘ declarat˘ local˘ unei metode va avea acces ¸i la a a a svariabilele finale declarate ˆ metoda respectiv˘. ın aclass ClasaDeAcoperire{ private int x=1; class ClasaImbricata1 { int a=x; } void metoda() { final int y=2; int z=3; class ClasaImbricata2 { int b=x; int c=y; int d=z; // Incorect } }} O clas˘ imbricat˘ membr˘ (care nu este local˘ unei metode) poate fi a a a areferit˘ din exteriorul clasei de acoperire folosind expresia a ClasaDeAcoperire.ClasaImbricataA¸adar, clasele membru pot fi declarate cu modificatorii public, protected, sprivate pentru a controla nivelul lor de acces din exterior, ˆ ıntocmai ca oricevariabil˘ sau metod˘ mebr˘ a clasei. Pentru clasele imbricate locale unei a a ametode nu sunt permi¸i acesti modificatori. s ¸ Toate clasele imbricate pot fi declarate folosind modificatorii abstract ¸i sfinal, semnificatia lor fiind aceea¸i ca ¸i ˆ cazul claselor obi¸nuite. ¸ s s ın s
  • 67. 66 CAPITOLUL 2. OBIECTE SI CLASE ¸2.6.2 Clase interneSpre deosebire de clasele obi¸nuite, o clas˘ imbricat˘ poate fi declarat˘ static˘ s a a a asau nu. O clas˘ imbricat˘ nestatic˘ se nume¸te clasa intern˘. a a a s aclass ClasaDeAcoperire{ ... class ClasaInterna { ... } static class ClasaImbricataStatica { ... }} Diferentierea acestor denumiri se face deoarece: ¸ • o ”clas˘ imbricat˘” reflect˘ relatia sintactic˘ a dou˘ clase: codul unei a a a ¸ a a clase apare ˆ interiorul codului altei clase; ın • o ”clas˘ intern˘” reflect˘ relatia dintre instantele a dou˘ clase, ˆ sensul a a a ¸ ¸ a ın c˘ o instanta a unei clase interne nu poate exista decat ˆ cadrul unei a ¸ ın instante a clasei de acoperire. ¸ In general, cele mai folosite clase imbricate sunt cele interne. A¸adar, o clas˘ intern˘ este o clas˘ imbricat˘ ale carei instante nu pot s a a a a ¸exista decˆt ˆ cadrul instantelor clasei de acoperire ¸i care are acces direct a ın ¸ sla toti membrii clasei sale de acoperire. ¸2.6.3 Identificare claselor imbricateDup˘ cum ¸tim orice clas˘ produce la compilare a¸a numitele ”unit˘¸i de com- a s a s atpilare”, care sunt fi¸iere avˆnd numele clasei respective ¸i extensia .class s a s¸i care contin toate informatiile despre clasa respectiv˘. Pentru clasele im-s ¸ ¸ abricate aceste unit˘¸i de compilare sunt denumite astfel: numele clasei de atacoperire, urmat de simbolul ’$’ apoi de numele clasei imbricate.class ClasaDeAcoperire{ class ClasaInterna1 {} class ClasaInterna2 {}}
  • 68. 2.7. CLASE SI METODE ABSTRACTE ¸ 67 Pentru exemplul de mai sus vor fi generate trei fi¸iere: s ClasaDeAcoperire.class ClasaDeAcoperire$ClasaInterna1.class ClasaDeAcoperire$ClasaInterna2.class In cazul ˆ care clasele imbricate au la rˆndul lor alte clase imbricate ın a(situatie mai putin uzual˘) denumirea lor se face dup˘ aceea¸i regul˘: ad˘ugarea ¸ ¸ a a s a aunui ’$’ ¸i apoi numele clasei imbricate. s2.6.4 Clase anonimeExist˘ posibilitatea definirii unor clase imbricate locale, f˘r˘ nume, utilizate a aadoar pentru instantierea unui obiect de un anumit tip. Astfel de clase se ¸numesc clase anonime ¸i sunt foarte utile ˆ situatii cum ar fi crearea unor s ın ¸obiecte ce implementeaz˘ o anumit˘ interfat˘ sau extind o anumit˘ clas˘ a a ¸a a aabstract˘. a Exemple de folosire a claselor anonime vor fi date ˆ capitolul ”Interfete”, ın ¸precum ¸i extensiv ˆ capitolul ”Interfata grafic˘ cu utilizatorul”. s ın ¸ a Fi¸ierele rezultate ˆ urma compil˘rii claselor anonime vor avea numele s ın ade forma ClasaAcoperire.$1,..., ClasaAcoperire.$n, unde n este num˘rul ade clase anonime definite ˆ clasa respectiv˘ de acoperire. ın a2.7 Clase ¸i metode abstracte sUneori ˆ proiectarea unei aplicatii este necesar s˘ reprezent˘m cu ajutorul ın ¸ a aclaselor concepte abstracte care s˘ nu poat˘ fi instantiate ¸i care s˘ foloseasc˘ a a ¸ s a adoar la dezvoltarea ulterioar˘ a unor clase ce descriu obiecte concrete. De ex- aemplu, ˆ pachetul java.lang exist˘ clasa abstract˘ Number care modeleaz˘ ın a a aconceptul generic de ”num˘r”. Intr-un program nu avem ˆ a nevoie de nu- a ıns˘mere generice ci de numere de un anumit tip: ˆ ıntregi, reale, etc. Clasa Numberserve¸te ca superclas˘ pentru clasele concrete Byte, Double, Float, Integer, s aLong ¸i Short, ce implementeaz˘ obiecte pentru descrierea numerelor de un s aanumit tip. A¸adar, clasa Number reprezint˘ un concept abstract ¸i nu vom s a sputea instantia obiecte de acest tip - vom folosi ˆ schimb subclasele sale. ¸ ın Number numar = new Number(); // Eroare Integer intreg = new Integer(10); // Corect
  • 69. 68 CAPITOLUL 2. OBIECTE SI CLASE ¸2.7.1 Declararea unei clase abstracteDeclararea unei clase abstracte se face folosind cuvˆntul rezervat abstract: a [public] abstract class ClasaAbstracta [extends Superclasa] [implements Interfata1, Interfata2, ...] { // Declaratii uzuale // Declaratii de metode abstracte} O clas˘ abstract˘ poate avea modificatorul public, accesul implicit fiind a ala nivel de pachet, dar nu poate specifica modificatorul final, combinatia ¸abstract final fiind semnalat˘ ca eroare la compilare - de altfel, o clas˘ a adeclarat˘ astfel nu ar avea nici o utilitate. a O clas˘ abstract˘ poate contine acelea¸i elemente membre ca o clas˘ a a ¸ s aobi¸nuit˘, la care se adaug˘ declaratii de metode abstracte - f˘r˘ nici o im- s a a ¸ aaplementare.2.7.2 Metode abstracteSpre deosebire de clasele obi¸nuite care trebuie s˘ furnizeze implement˘ri s a apentru toate metodele declarate, o clas˘ abstract˘ poate contine metode f˘r˘ a a ¸ aanici o implementare. Metodele fara nici o implementare se numesc metodeabstracte ¸i pot ap˘rea doar ˆ clase abstracte. In fata unei metode abstracte s a ın ¸trebuie s˘ apar˘ obligatoriu cuvˆntul cheie abstract, altfel va fi furnizat˘ o a a a aeroare de compilare.abstract class ClasaAbstracta { abstract void metodaAbstracta(); // Corect void metoda(); // Eroare} In felul acesta, o clas˘ abstract˘ poate pune la dispozitia subclaselor sale a a ¸un model complet pe care trebuie s˘-l implementeze, furnizˆnd chiar imple- a amentarea unor metode comune tuturor claselor ¸i l˘sˆnd explicitarea altora s aa
  • 70. 2.7. CLASE SI METODE ABSTRACTE ¸ 69fiec˘rei subclase ˆ parte. a ınUn exemplu elocvent de folosire a claselor ¸i metodelor abstracte este de- sscrierea obiectelor grafice ˆ ıntr-o manier˘ orientat˘-obiect. a a • Obiecte grafice: linii, dreptunghiuri, cercuri, curbe Bezier, etc • St˘ri comune: pozitia(originea), dimensiunea, culoarea, etc a ¸ • Comportament: mutare, redimensionare, desenare, colorare, etc. Pentru a folosi st˘rile ¸i comportamentele comune acestor obiecte ˆ avan- a s ıntajul nostru putem declara o clas˘ generic˘ GraphicObject care s˘ fie su- a a aperclas˘ pentru celelalte clase. Metodele abstracte vor fi folosite pentru im- aplementarea comportamentului specific fiec˘rui obiect, cum ar fi desenarea aiar cele obi¸nuite pentru comportamentul comun tuturor, cum ar fi schim- sbarea originii. Implementarea clasei abstracte GraphicObject ar putea ar˘ta aastfel: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(); ...}
  • 71. 70 CAPITOLUL 2. OBIECTE SI CLASE ¸ O subclas˘ care nu este abstract˘ a unei clase abstracte trebuie s˘ furnizeze a a aobligatoriu implement˘ri ale metodelor abstracte definite ˆ superclas˘. Im- a ın aplementarea claselor pentru obiecte grafice ar fi:class Circle extends GraphicObject { void draw() { // Obligatoriu implementarea ... }}class Rectangle extends GraphicObject { void draw() { // Obligatoriu implementarea ... }} Legat de metodele abstracte, mai trebuie mentionate urm˘toarele: ¸ a • O clas˘ abstract˘ poate s˘ nu aib˘ nici o metod˘ abstract˘. a a a a a a • O metod˘ abstract˘ nu poate ap˘rea decˆt ˆ a a a a ıntr-o clas˘ abstract˘. a a • Orice clas˘ care are o metod˘ abstract˘ trebuie declarat˘ ca fiind ab- a a a a stract˘. a In API-ul oferit de platforma de lucru Java sunt numeroase exemple deierarhii care folosesc la nivelele superioare clase abstracte. Dintre cele maiimportante amintim: • Number: superclasa abstract˘ a tipurilor referint˘ numerice a ¸a • Reader, Writer: superclasele abstracte ale fluxurilor de intrare/ie¸ire s pe caractere • InputStream, OutputStream: superclasele abstracte ale fluxurilor de intrare/ie¸ire pe octeti s ¸ • AbstractList, AbstractSet, AbstractMap: superclase abstracte pen- tru structuri de date de tip colectie ¸
  • 72. 2.8. CLASA OBJECT 71 • Component : superclasa abstract˘ a componentelor folosite ˆ dez- a ın voltarea de aplicatii cu interfat˘ grafic˘ cu utilizatorul (GUI), cum ar ¸ ¸a a fi Frame, Button, Label, etc. • etc.2.8 Clasa Object2.8.1 Orice clas˘ are o superclas˘ a aDup˘ cum am v˘zut ˆ sectiunea dedicat˘ modalit˘¸ii de creare a unei clase, a a ın ¸ a atclauza ”extends” specific˘ faptul c˘ acea clas˘ extinde (mo¸tene¸te) o alt˘ a a a s s aclas˘, numit˘ superclas˘. O clas˘ poate avea o singur˘ superclas˘ (Java nu a a a a a asuport˘ mo¸tenirea multipl˘) ¸i chiar dac˘ nu specific˘m clauza ”extends” la a s a s a acrearea unei clase ea totu¸i va avea o superclas˘. Cu alte cuvinte, ˆ Java s a ınorice clas˘ are o superclas˘ ¸i numai una. Evident, trebuie s˘ existe o exceptie a as a ¸de la aceast˘ regul˘ ¸i anume clasa care reprezint˘ r˘d˘cina ierarhiei format˘ a as a a a ade relatiile de mo¸tenire dintre clase. Aceasta este clasa Object. ¸ s Clasa Object este ¸i superclasa implicit˘ a claselor care nu specific˘ o s a aanumit˘ superclas˘. Declaratiile de mai jos sunt echivalente: a a ¸class Exemplu {}class Exemplu extends Object {}2.8.2 Clasa ObjectClasa Object este cea mai general˘ dintre clase, orice obiect fiind, direct asau indirect, descendent al acestei clase. Fiind p˘rintele tuturor, Object adefine¸te ¸i implementeaz˘ comportamentul comun al tuturor celorlalte clase s s aJava, cum ar fi: • posibilitatea test˘rii egalit˘¸ii valorilor obiectelor, a at • specificarea unei reprezent˘ri ca ¸ir de caractere a unui obiect , a s • returnarea clasei din care face parte un obiect, • notificarea altor obiecte c˘ o variabil˘ de conditie s-a schimbat, etc. a a ¸
  • 73. 72 CAPITOLUL 2. OBIECTE SI CLASE ¸ Fiind subclas˘ a lui Object, orice clas˘ ˆ poate supradefini metodele a a ıicare nu sunt finale. Metodele cel mai uzual supradefinite sunt: clone,equals/hashCode, finalize, toString. • clone Aceast˘ metod˘ este folosit˘ pentru duplicarea obiectelor (crearea unor a a a clone). Clonarea unui obiect presupune crearea unui nou obiect de acela¸i tip ¸i care s˘ aib˘ aceea¸i stare (acelea¸i valori pentru variabilele s s a a s s sale). • equals, hashCode Acestea sunt, de obicei, supradefinite ˆ ımpreun˘. In metoda equals este a scris codul pentru compararea egalit˘¸ii continutului a dou˘ obiecte. at ¸ a Implicit (implementarea din clasa Object), aceast˘ metod˘ compar˘ a a a referintele obiectelor. Uzual este redefinit˘ pentru a testa dac˘ st˘rile ¸ a a a obiectelor coincid sau dac˘ doar o parte din variabilele lor coincid. a Metoda hashCode returneaza un cod ˆ ıntreg pentru fiecare obiect, pen- tru a testa consistenta obiectelor: acela¸i obiect trebuie s˘ returneze ¸ s a acela¸i cod pe durata executiei programului. s ¸ Dac˘ dou˘ obiecte sunt egale conform metodei equals, atunci apelul a a metodei hashCode pentru fiecare din cele dou˘ obiecte ar trebui s˘ a a returneze acela¸i intreg. s • finalize In aceast˘ metod˘ se scrie codul care ”cur˘¸˘ dup˘ un obiect” ˆ a a ata a ınainte de a fi eliminat din memorie de colectorul de gunoaie. (vezi ”Distrugerea obiectelor”) • toString Este folosit˘ pentru a returna o reprezentare ca ¸ir de caractere a unui a s obiect. Este util˘ pentru concatenarea ¸irurilor cu diverse obiecte ˆ a s ın vederea afi¸˘rii, fiind apelat˘ automat atunci cˆnd este necesar˘ trans- sa a a a formarea unui obiect ˆ ¸ir de caractere. ın s Exemplu obj = new Exemplu(); System.out.println("Obiect=" + obj); //echivalent cu System.out.println("Obiect=" + obj.toString());
  • 74. 2.8. CLASA OBJECT 73 S˘ consider˘m urm˘torul exemplu, ˆ care implement˘m partial clasa a a a ın a ¸numerelor complexe, ¸i ˆ care vom supradefini metode ale clasei Object. s ınDe asemenea, vom scrie un mic program TestComplex ˆ care vom testa ınmetodele clasei definite. Listing 2.1: Clasa numerelor complexeclass 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 ; }}
  • 75. 74 CAPITOLUL 2. OBIECTE SI CLASE ¸public class TestComplex { public static void main ( String c []) { Complex c1 = new Complex (1 ,2) ; Complex c2 = new Complex (2 ,3) ; Complex c3 = ( Complex ) c1 . clone () ; System . out . println ( c1 . aduna ( c2 ) ) ; // 3.0 + 5.0 i System . out . println ( c1 . equals ( c2 ) ) ; // false System . out . println ( c1 . equals ( c3 ) ) ; // true }}2.9 Conversii automate ˆ ıntre tipuriDup˘ cum v˘zut tipurile Java de date pot fi ˆ artie ˆ primitive ¸i referint˘. a a ımp˘ ¸ ın s ¸aPentru fiecare tip primitiv exist˘ o clas˘ corespunz˘toare care permie lucrul a a aorientat obiect cu tipul respectiv. byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean Fiecare din aceste clase are un constructor ce permite initializarea unui ¸obiect avˆnd o anumit˘ valoare primitiv˘ ¸i metode specializate pentru con- a a asversia unui obiect ˆ tipul primitiv corespunz˘tor, de genul tipPrimitivValue: ın a Integer obi = new Integer(1); int i = obi.intValue(); Boolean obb = new Boolean(true); boolean b = obb.booleanValue(); Incepˆnd cu versiunea 1.5 a limbajului Java, atribuirile explicite ˆ a ıntretipuri primitve ¸i referint˘ sunt posibile, acest mecanism purtˆnd numele de s ¸a aautoboxing, respectiv auto-unboxing. Conversia explicit˘ va fi facut˘ de c˘tre a a acompilator.
  • 76. 2.10. TIPUL DE DATE ENUMERARE 75 // Doar de la versiunea 1.5 ! Integer obi = 1; int i = obi; Boolean obb = true; boolean b = obb;2.10 Tipul de date enumerareIncepˆnd cu versiunea 1.5 a limbajului Java, exist˘ posibilitatea de a defini a atipuri de date enumerare prin folosirea cuvˆntului cheie enum. Acest˘ a asolutie simplific˘ manevrarea grupurilor de constante, dup˘ cum reiese din ¸ a aurm˘torul exemplu: a public class CuloriSemafor { public static final int ROSU = -1; public static final int GALBEN = 0; public static final int VERDE = 1; } ... // Exemplu de utilizare if (semafor.culoare = CuloriSemafor.ROSU) semafor.culoare = CuloriSemafor.GALBEN); ... Clasa de mai sus poate fi rescris˘ astfel: a public enum CuloriSemafor { ROSU, GALBEN, VERDE }; ... // Utilizarea structurii se face la fel ... if (semafor.culoare = CuloriSemafor.ROSU) semafor.culoare = CuloriSemafor.GALBEN); ... Compilatorul este responsabil cu transformarea unei astfel de structuriˆıntr-o clas˘ corespunz˘toare. a a
  • 77. 76 CAPITOLUL 2. OBIECTE SI CLASE ¸
  • 78. Capitolul 3Exceptii ¸3.1 Ce sunt exceptiile ? ¸Termenul exceptie este o prescurtare pentru ”eveniment exceptional” ¸i poate ¸ ¸ sfi definit ca un eveniment ce se produce ˆ timpul executiei unui program ¸i ın ¸ scare provoac˘ ˆ a ıntreruperea cursului normal al executiei acestuia. ¸ Exceptiile pot ap˘rea din diverse cauze ¸i pot avea nivele diferite de grav- ¸ a sitate: de la erori fatale cauzate de echipamentul hardware pˆn˘ la erori ce a a¸in strict de codul programului, cum ar fi accesarea unui element din afaratspatiului alocat unui vector. ¸ In momentul cˆnd o asemenea eroare se produce ˆ timpul executiei va fi a ın ¸generat un obiect de tip exceptie ce contine: ¸ ¸ • informatii despre exceptia respectiv˘; ¸ ¸ a • starea programului ˆ momentul producerii acelei exceptii. ın ¸public class Exemplu { public static void main(String args[]) { int v[] = new int[10]; v[10] = 0; //Exceptie ! System.out.println("Aici nu se mai ajunge..."); }} La rularea programului va fi generat˘ o exceptie, programul se va opri a ¸la instructiunea care a cauzat exceptia ¸i se va afi¸a un mesaj de eroare de ¸ ¸ s sgenul: 77
  • 79. 78 CAPITOLUL 3. EXCEPTII ¸ "Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException :10 at Exceptii.main (Exceptii.java:4)" Crearea unui obiect de tip exceptie se nume¸te aruncarea unei exceptii ¸ s ¸(”throwing an exception”). In momentul ˆ care o metod˘ genereaz˘ (arunc˘) ın a a ao exceptie sistemul de executie este responsabil cu g˘sirea unei secvente de ¸ ¸ a ¸cod dintr-o metod˘ care s˘ o trateze. C˘utarea se face recursiv, ˆ a a a ıncepˆnd cu ametoda care a generat exceptia ¸i mergˆnd ˆ ¸ s a ınapoi pe linia apelurilor c˘treaacea metod˘. a Secventa de cod dintr-o metod˘ care trateaz˘ o anumit˘ exceptie se ¸ a a a ¸nume¸te analizor de exceptie (”exception handler”) iar interceptarea ¸i tratarea s ¸ sei se nume¸te prinderea exceptiei (”catch the exception”). s ¸Cu alte cuvinte, la aparitia unei erori este ”aruncat˘” o exceptie iar cineva ¸ a ¸trebuie s˘ o ”prind˘” pentru a o trata. Dac˘ sistemul nu gase¸te nici un a a a sanalizor pentru o anumit˘ exceptie, atunci programul Java se opre¸te cu un a ¸ smesaj de eroare (ˆ cazul exemplului de mai sus mesajul ”Aici nu se mai ınajunge...” nu va fi afi¸at). s Atentie ¸ In Java tratarea erorilor nu mai este o optiune ci o constrˆngere. In ¸ aaproape toate situatile, o secvent˘ de cod care poate provoca exceptii trebuie ¸ ¸a ¸s˘ specifice modalitatea de tratare a acestora. a3.2 ”Prinderea” ¸i tratarea exceptiilor s ¸Tratarea exceptiilor se realizeaz˘ prin intermediul blocurilor de instructiuni ¸ a ¸try, catch ¸i finally. O secvent˘ de cod care trateaz˘ anumite exceptii s ¸a a ¸trebuie s˘ arate astfel: a try { // Instructiuni care pot genera exceptii } catch (TipExceptie1 variabila) { // Tratarea exceptiilor de tipul 1
  • 80. 3.2. ”PRINDEREA” SI TRATAREA EXCEPTIILOR ¸ ¸ 79 } catch (TipExceptie2 variabila) { // Tratarea exceptiilor de tipul 2 } . . . finally { // Cod care se executa indiferent // daca apar sau nu exceptii } S˘ consider˘m urm˘torul exemplu: citirea unui fi¸ier octet cu octet ¸i a a a s safisarea lui pe ecran. F˘r˘ a folosi tratarea exceptiilor metoda responsabil˘ aa ¸ acu citirea fi¸ierului ar ar˘ta astfel: s a public static void citesteFisier(String fis) { FileReader f = null; // Deschidem fisierul System.out.println("Deschidem fisierul " + fis); f = new FileReader(fis); // Citim si afisam fisierul caracter cu caracter int c; while ( (c=f.read()) != -1) System.out.print((char)c); // Inchidem fisierul System.out.println("nInchidem fisierul " + fis); f.close(); } Aceast˘ secvent˘ de cod va furniza erori la compilare deoarece ˆ Java a ¸a ıntratarea erorilor este obligatorie. Folosind mecanismul exceptiilor metoda ¸citeste ˆsi poate trata singur˘ erorile care pot surveni pe parcursul executiei ı¸ a ¸sale. Mai jos este codul complte ¸i corect al unui program ce afi¸eaz˘ pe ecran s s acontinutul unui fi¸ier al c˘rui nume este primit ca argument de la linia de ¸ s acomand˘. Tratarea exceptiilor este realizat˘ complet chiar de c˘tre metoda a ¸ a aciteste.
  • 81. 80 CAPITOLUL 3. EXCEPTII ¸ Listing 3.1: Citirea unui fisier - corectimport java . io .*;public class CitireFisier { public static void citesteFisier ( String fis ) { FileReader f = null ; try { // Deschidem fisierul System . out . println ( " Deschidem fisierul " + fis ) ; f = new FileReader ( fis ) ; // Citim si afisam fisierul caracter cu caracter int c ; while ( ( c = f . read () ) != -1) System . out . print (( char ) c ) ; } catch ( File Not F o u n d E x c e p t i o n e ) { // Tratam un tip de exceptie System . err . println ( " Fisierul nu a fost gasit ! " ) ; System . err . println ( " Exceptie : " + e . getMessage () ) ; System . exit (1) ; } catch ( IOException e ) { // Tratam alt tip de exceptie System . out . println ( " Eroare la citirea din fisier ! " ) ; e . printStackTrace () ; } finally { if ( f != null ) { // Inchidem fisierul System . out . println ( " nInchidem fisierul . " ) ; try { f . close () ; } catch ( IOException e ) { System . err . println ( " Fisierul nu poate fi inchis ! " ) ; e . printStackTrace () ; } } } } public static void main ( String args []) { if ( args . length > 0) citesteFisier ( args [0]) ; else
  • 82. 3.2. ”PRINDEREA” SI TRATAREA EXCEPTIILOR ¸ ¸ 81 System . out . println ( " Lipseste numele fisierului ! " ) ; }} Blocul ”try” contine instructiunile de deschidere a unui fi¸ier ¸i de citire ¸ s sdintr-un fi¸ier, ambele putˆnd produce exceptii. Exceptiile provocate de s a ¸ ¸aceste instructiuni sunt tratate ˆ cele dou˘ blocuri ”catch”, cˆte unul pen- ¸ ın a atru fiecare tip de exceptie. Inchiderea fi¸ierului se face ˆ blocul ”finally”, ¸ s ındeoarece acesta este sigur c˘ se va executa F˘r˘ a folosi blocul ”finally”, a aaˆınchiderea fi¸ierului ar fi trebuit facut˘ ˆ fiecare situatie ˆ care fi¸ierul ar fi s a ın ¸ ın sfost deschis, ceea ce ar fi dus la scrierea de cod redundant. try { ... // Totul a decurs bine. f.close(); } ... catch (IOException e) { ... // A aparut o exceptie la citirea din fisier f.close(); // cod redundant } O problem˘ mai delicat˘ care trebuie semnalata ˆ aceasta situatie este a a ın ¸faptul c˘ metoda close, responsabil˘ cu ˆ a a ınchiderea unui fi¸ier, poate provoca sla rˆndul s˘u exceptii, de exemplu atunci cˆnd fi¸ierul mai este folosit ¸i de a a ¸ a s salt proces ¸i nu poate fi ˆ s ınchis. Deci, pentru a avea un cod complet corecttrebuie s˘ trat˘m ¸i posibilitatea aparitiei unei exceptii la metoda close. a a s ¸ ¸ Atentie ¸ Obligatoriu un bloc de instructiuni ”try” trebuie s˘ fie urmat de unul ¸ asau mai multe blocuri ”catch”, ˆ functie de exceptiile provocate de acele ın ¸ ¸instructiuni sau (optional) de un bloc ”finally”. ¸ ¸
  • 83. 82 CAPITOLUL 3. EXCEPTII ¸3.3 ”Aruncarea” exceptiilor ¸In cazul ˆ care o metod˘ nu ˆsi asum˘ responsabilitatea trat˘rii uneia sau ın a ı¸ a amai multor exceptii pe care le pot provoca anumite instructiuni din codul ¸ ¸s˘u atunci ea poate s˘ ”arunce” aceste exceptii c˘tre metodele care o ape- a a ¸ aleaz˘, urmˆnd ca acestea s˘ implementeze tratarea lor sau, la rˆndul lor, s˘ a a a a a”arunce” mai departe exceptiile respective. ¸Acest lucru se realizeaz˘ prin specificarea ˆ declaratia metodei a clauzei a ın ¸throws: [modificatori] TipReturnat metoda([argumente]) throws TipExceptie1, TipExceptie2, ... { ... } Atentie ¸ O metod˘ care nu trateaz˘ o anumit˘ exceptie trebuie obligatoriu s˘ o a a a ¸ a”arunce”. In exemplul de mai sus dac˘ nu facem tratarea exceptiilor ˆ cadrul a ¸ ınmetodei citeste atunci metoda apelant˘ (main) va trebui s˘ fac˘ acest lucru: a a a Listing 3.2: Citirea unui fisierimport 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 () ; }
  • 84. 3.3. ”ARUNCAREA” EXCEPTIILOR ¸ 83 public static void main ( String args []) { if ( args . length > 0) { try { citesteFisier ( args [0]) ; } catch ( File NotFo undE x c ep ti on e ) { System . err . println ( " Fisierul nu a fost gasit ! " ) ; System . err . println ( " Exceptie : " + e ) ; } catch ( IOException e ) { System . out . println ( " Eroare la citirea din fisier ! " ) ; e . printStackTrace () ; } } else System . out . println ( " Lipseste numele fisierului ! " ) ; }} Observati c˘, ˆ acest caz, nu mai putem diferentia exceptiile provocate de ¸ a ın ¸ ¸citirea din fi¸ier s de inchiderea fi¸ierului, ambele fiind de tipul IOException. s sDe asemenea, inchiderea fi¸ierului nu va mai fi facut˘ ˆ situatia ˆ care s a ın ınapare o exceptie la citirea din fi¸ier. Este situatia ˆ care putem folosi blocul ¸ s ¸ ınfinally f˘r˘ a folosi nici un bloc catch: aa public static void citesteFisier(String fis) throws FileNotFoundException, IOException { FileReader f = null; try { f = new FileReader(numeFisier); int c; while ( (c=f.read()) != -1) System.out.print((char)c); } finally { if (f!=null) f.close(); } }
  • 85. 84 CAPITOLUL 3. EXCEPTII ¸ Metoda apelant˘ poate arunca la rˆndul sˆu exceptiile mai departe c˘tre a a a ¸ ametoda care a apelat-o la rˆndul ei. Aceast˘ ˆ antuire se termin˘ cu metoda a a ınl˘ ¸ amain care, dac˘ va arunca exceptiile ce pot ap˘rea ˆ corpul ei, va determina a ¸ a ıntrimiterea exceptiilor c˘tre ma¸ina virtual˘ Java. ¸ a s a public void metoda3 throws TipExceptie { ... } public void metoda2 throws TipExceptie { metoda3(); } public void metoda1 throws TipExceptie { metoda2(); } public void main throws TipExceptie { metoda1(); } Tratarea exceptiilor de c˘tre JVM se face prin terminarea programului ¸i ¸ a safi¸area informatiilor despre exceptia care a determinat acest lucru. Pentru s ¸ ¸exemplul nostru, metoda main ar putea fi declarat˘ astfel: a public static void main(String args[]) throws FileNotFoundException, IOException { citeste(args[0]); } Intotdeauna trebuie g˘sit compromisul optim ˆ a ıntre tratarea local˘ a exceptiilor a ¸¸i aruncarea lor c˘tre nivelele superioare, astfel ˆ at codul s˘ fie cˆt mai clars a ıncˆ a a¸i identificarea locului ˆ care a ap˘rut exceptia s˘ fie cˆt mai u¸or de f˘cut.s ın a ¸ a a s a Aruncarea unei exceptii se poate face ¸i implicit prin instructiunea throw ¸ s ¸ce are formatul: throw exceptie, ca ˆ exemplele de mai jos: ın throw new IOException("Exceptie I/O"); ... if (index >= vector.length) throw new ArrayIndexOutOfBoundsException(); ...
  • 86. ˘3.4. AVANTAJELE TRATARII EXCEPTIILOR ¸ 85 catch(Exception e) { System.out.println("A aparut o exceptie); throw e; }Aceast˘ instructiune este folosit˘ mai ales la aruncarea exceptiilor proprii. a ¸ a ¸(vezi ”Crearea propriilor exceptii”) ¸3.4 Avantajele trat˘rii exceptiilor a ¸Prin modalitatea sa de tratare a exceptiilor, Java are urm˘toarele avantaje ¸ afat˘ de mecanismul traditional de tratare a erorilor: ¸a ¸ • Separarea codului pentru tratarea unei erori de codul ˆ care ea poate ın s˘ apar˘. a a • Propagarea unei erori pˆn˘ la un analizor de exceptii corespunz˘tor. a a ¸ a • Gruparea erorilor dup˘ tipul lor. a3.4.1 Separarea codului pentru tratarea erorilorIn programarea traditional˘ tratarea erorilor se combin˘ cu codul ce poate ¸ a aproduce aparitia lor producˆnd a¸a numitul ”cod spaghetti”. S˘ consider˘m ¸ a s a aurm˘torul exemplu: o functie care ˆ a ¸ ıncarc˘ un fi¸ier ˆ memorie: a s ın citesteFisier { deschide fisierul; determina dimensiunea fisierului; aloca memorie; citeste fisierul in memorie; inchide fisierul;} Problemele care pot ap˘rea la aceasta functie, aparent simpl˘, sunt de a ¸ agenul: ”Ce se ˆ ampl˘ dac˘: ... ?” ıntˆ a a • fi¸ierul nu poate fi deschis s • nu se poate determina dimensiunea fi¸ierului s
  • 87. 86 CAPITOLUL 3. EXCEPTII ¸ • nu poate fi alocat˘ suficient˘ memorie a a • nu se poate face citirea din fi¸ier s • fi¸ierul nu poate fi ˆ s ınchis Un cod traditional care s˘ trateze aceste erori ar ar˘ta astfel: ¸ a a int citesteFisier() { int codEroare = 0; deschide fisierul; if (fisierul s-a deschis) { determina dimensiunea fisierului; if (s-a determinat dimensiunea) { aloca memorie; if (s-a alocat memorie) { citeste fisierul in memorie; if (nu se poate citi din fisier) { codEroare = -1; } } else { codEroare = -2; } } else { codEroare = -3; } inchide fisierul; if (fisierul nu s-a inchis && codEroare == 0) { codEroare = -4; } else { codEroare = codEroare & -4; } } else { codEroare = -5; } return codEroare; } // Cod "spaghetti" Acest stil de progamare este extrem de susceptibil la erori ¸i ˆ s ıngreuneaz˘ aextrem de mult ˆ ¸telegerea sa. In Java, folosind mecansimul exceptiilor, ınt ¸codul ar arata, schematizat, astfel:
  • 88. ˘3.4. AVANTAJELE TRATARII EXCEPTIILOR ¸ 87 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˘. a3.4.2 Propagarea erorilorPropagarea unei erori se face pˆn˘ la un analizor de exceptii corespunz˘tor. a a ¸ aS˘ presupunem c˘ apelul la metoda citesteFisier este consecinta unor a a ¸apeluri imbricate de metode: int metoda1() { metoda2(); ... } int metoda2() { metoda3; ... } int metoda3 { citesteFisier(); ...
  • 89. 88 CAPITOLUL 3. EXCEPTII ¸ } S˘ presupunem de asemenea c˘ dorim s˘ facem tratarea erorilor doar a a aˆ metoda1. Traditional, acest lucru ar trebui f˘cut prin propagarea eroriiın ¸ aproduse de metoda citesteFisier pˆn˘ la metoda1: a a int metoda1() { int codEroare = metoda2(); if (codEroare != 0) //proceseazaEroare; ... } int metoda2() { int codEroare = metoda3(); if (codEroare != 0) return codEroare; ... } int metoda3() { int codEroare = citesteFisier(); if (codEroare != 0) return codEroare; ... } Dup˘ cum am vazut, Java permite unei metode s˘ arunce exceptiile a a ¸ap˘rute ˆ cadrul ei la un nivel superior, adic˘ functiilor care o apeleaz˘ a ın a ¸ asau sistemului. Cu alte cuvinte, o metod˘ poate s˘ nu ˆsi asume responsabil- a a ı¸itatea trat˘rii exceptiilor ap˘rute ˆ cadrul ei: a ¸ a ın int metoda1() { try { metoda2(); } catch (TipExceptie e) { //proceseazaEroare; } ... }
  • 90. ˘3.4. AVANTAJELE TRATARII EXCEPTIILOR ¸ 89 int metoda2() throws TipExceptie { metoda3(); ... } int metoda3() throws TipExceptie { citesteFisier(); ... }3.4.3 Gruparea erorilor dup˘ tipul lor aIn Java exist˘ clase corespunz˘toare tuturor exceptiilor care pot ap˘rea la a a ¸ aexecutia unui program. Acestea sunt grupate ˆ functie de similarit˘¸ile ¸ ın ¸ atlor ˆıntr-o ierarhie de clase. De exemplu, clasa IOException se ocup˘ cu aexceptiile ce pot ap˘rea la operatii de intrare/iesire ¸i diferentiaz˘ la rˆndul ¸ a ¸ s ¸ a aei alte tipuri de exceptii, cum ar fi FileNotFoundException, EOFException, ¸etc.La rˆndul ei, clasa IOException se ˆ a ıncadreaz˘ ˆ a ıntr-o categorie mai larg˘ de aexceptii ¸i anume clasa Exception. ¸ sRadacin˘ acestei ierarhii este clasa Throwable (vezi ”Ierarhia claselor ce de- ascriu exceptii”). ¸ Pronderea unei exceptii se poate face fie la nivelul clasei specifice pen- ¸tru acea exceptie, fie la nivelul uneia din superclasele sale, ˆ functie de ¸ ın ¸necesit˘¸ile programului, ˆ a, cu cˆt clasa folosit˘ este mai generic˘ cu atˆt at ıns˘ a a a atratarea exceptiilor programul ˆsi pierde din flexibilitate. ¸ ı¸ 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
  • 91. 90 CAPITOLUL 3. EXCEPTII ¸ catch (IOException e) { // Exceptie generica provocata de o operatie IO } // sau catch (Exception e) { // Cea mai generica exceptie soft } //sau catch (Throwable e) { // Superclasa exceptiilor }3.5 Ierarhia claselor ce descriu exceptii ¸R˘d˘cina claselor ce descriu exceptii este clasa Throwable iar cele mai impor- a a ¸tante subclase ale sale sunt Error, Exception ¸i RuntimeException, care ssunt la rˆndul lor superclase pentru o serie ˆ a ıntreag˘ de tipuri de exceptii. a ¸ Erorile, obiecte de tip Error, sunt cazuri speciale de exceptii generate ¸de functionarea anormal˘ a echipamentului hard pe care ruleaz˘ un pro- ¸ a agram Java ¸i sunt invizibile programatorilor. Un program Java nu trebuie s˘ s atrateze aparitia acestor erori ¸i este improbabil ca o metod˘ Java s˘ provoace ¸ s a aasemenea erori.
  • 92. 3.6. EXCEPTII LA EXECUTIE ¸ ¸ 91 Exceptiile, obiectele de tip Exception, sunt exceptiile standard (soft) care ¸ ¸trebuie tratate de c˘tre programele Java. Dup˘ cum am mai zis tratarea aces- a ator exceptii nu este o optiune ci o constrˆngere. Exceptiile care pot ”sc˘pa” ¸ ¸ a ¸ anetratate descind din subclasa RuntimeException ¸i se numesc exceptii la s ¸executie. ¸ Metodele care sunt apelate uzual pentru un obiect exceptie sunt definite ¸ˆ clasa Throwable ¸i sunt publice, astfel ˆ at pot fi apelate pentru orice tipın s ıncˆde exceptie. Cele mai uzuale sunt: ¸ • getMessage - afi¸eaz˘ detaliul unei exceptii; s a ¸ • printStackTrace - afi¸eaz˘ informatii complete despre exceptie ¸i lo- s a ¸ ¸ s calizarea ei; • toString - metod˘ mo¸tenit˘ din clasa Object, care furnizeaz˘ reprezentarea a s a a ca ¸ir de caractere a exceptiei. s ¸3.6 Exceptii la executie ¸ ¸In general, tratarea exceptiilor este obligatorie ˆ Java. De la acest principu se ¸ ınsustrag ˆ a a¸a numitele exceptii la executie sau, cu alte cuvinte, exceptiile ıns˘ s ¸ ¸ ¸care provin strict din vina programatorului ¸i nu generate de o anumit˘ s asituatie extern˘, cum ar fi lipsa unui fi¸ier. ¸ a sAceste exceptii au o superclas˘ comun˘ RuntimeException ¸i ˆ acesata ¸ a a s ıncategorie sunt incluse exceptiile provocate de: ¸ • operatii aritmetice ilegale (ˆ artirea ˆ ¸ ımpˆ ¸ ıntregilor la zero); ArithmeticException • accesarea membrilor unui obiect ce are valoarea null; NullPointerException • accesarea eronat˘ a elementelor unui vector. a ArrayIndexOutOfBoundsException Exceptiile la executie pot ap˘rea uriunde ˆ program ¸i pot fi extrem ¸ ¸ a ın sde numeroare iar ˆ ıncercarea de ”prindere” a lor ar fi extrem de anevoioas˘. aDin acest motiv, compilatorul permite ca aceste exceptii s˘ r˘mˆn˘ netratate, ¸ a a a atratarea lor nefiind ˆ a ilegal˘. Reamintim ˆ a c˘, ˆ cazul aparitiei oric˘rui ıns˘ a ıns˘ a ın ¸ atip de exceptie care nu are un analizor corespunz˘tor, programul va fi termi- ¸ anat.
  • 93. 92 CAPITOLUL 3. EXCEPTII ¸ int v[] = new int[10]; try { v[10] = 0; } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Atentie la indecsi!"); e.printStackTrace(); } // Corect, programul continua v[11] = 0; /* Nu apare eroare la compilare dar apare exceptie la executie si programul va fi terminat. */ System.out.println("Aici nu se mai ajunge..."); Imp˘rtirea la 0 va genera o exceptie doar dac˘ tipul numerelor ˆ artite a¸ ¸ a ımp˘ ¸este aritmetic ˆ ıntreg. In cazul tipurilor reale (float ¸i double) nu va fi sgenerat˘ nici o exceptie, ci va fi furnizat ca rezultat o constant˘ care poate a ¸ afi, functie de operatie, Infinity, -Infinity, sau Nan. ¸ int a=1, int b=0; System.out.println(a/b); // Exceptie la executie ! double x=1, y=-1, z=0; System.out.println(x/z); // Infinity System.out.println(y/z); // -Infinity System.out.println(z/z); // NaN3.7 Crearea propriilor exceptii ¸Adeseori poate ap˘rea necesitatea cre˘rii unor exceptii proprii pentru a pune a a ¸ˆ evidenta cazuri speciale de erori provocate de metodele claselor unei libr˘rii,ın ¸ acazuri care nu au fost prevazute ˆ ierarhia exceptiilor standard Java. ın ¸O exceptie proprie trebuie s˘ se ˆ ¸ a ıncadreze ˆ a ˆ ierarhia exceptiilor Java, ıns˘ ın ¸cu alte cuvinte clasa care o implementeaz˘ trebuie s˘ fie subclas˘ a uneia a a adeja existente ˆ aceasta ierarhie, preferabil una apropiat˘ ca semnificatie, ın a ¸sau superclasa Exception.
  • 94. 3.7. CREAREA PROPRIILOR EXCEPTII ¸ 93public class ExceptieProprie extends Exception { public ExceptieProprie(String mesaj) { super(mesaj); // Apeleaza constructorul superclasei Exception }} S˘ consider˘m urm˘torul exemplu, ˆ care cre˘m o clas˘ ce descrie partial a a a ın a a ¸o stiv˘ de numere ˆ a ıntregi cu operatiile de ad˘ugare a unui element, respec- ¸ ativ de scoatere a elementului din vˆrful stivei. Dac˘ presupunem c˘ stiva a a apoate memora maxim 100 de elemente, ambele operatii pot provoca exceptii. ¸ ¸Pentru a personaliza aceste exceptii vom crea o clas˘ specific˘ denumit˘ ¸ a a aExceptieStiva: Listing 3.3: Exceptii propriiclass ExceptieStiva extends Exception { public ExceptieStiva ( String mesaj ) { super ( mesaj ) ; }}class Stiva { int elemente [] = new int [100]; int n =0; // numarul de elemente din stiva public void adauga ( int x ) throws ExceptieStiva { if ( n ==100) throw new ExceptieStiva ( " Stiva este plina ! " ) ; elemente [ n ++] = x ; } public int scoate () throws ExceptieStiva { if ( n ==0) throw new ExceptieStiva ( " Stiva este goala ! " ) ; return elemente [n - -]; }} Secventa cheie este extends Exception care specific˘ faptul c˘ noua ¸ a aclas˘ ExceptieStiva este subclas˘ a clasei Exception ¸i deci implementeaz˘ a a s aobiecte ce reprezint˘ exceptii. a ¸
  • 95. 94 CAPITOLUL 3. EXCEPTII ¸In general, codul ad˘ugat claselor pentru exceptii proprii este nesemnificativ: a ¸unul sau doi constructori care afi¸eaza un mesaj de eroare la ie¸irea standard. s sProcesul de creare a unei noi exceptii poate fi dus mai departe prin ad˘ugarea ¸ aunor noi metode clasei ce descrie acea exceptie, ˆ a aceasta dezvoltare nu ¸ ıns˘ˆsi are rostul ˆ majoritatea cazurilor. Exceptiile proprii sunt descrise uzualı¸ ın ¸de clase foarte simple, chiar f˘r˘ nici un cod ˆ ele, cum ar fi: aa ın class ExceptieSimpla extends Exception { } Aceast˘ clas˘ se bazeaz˘ pe constructorul implicit creat de compilator a a aˆ a nu are constructorul ExceptieSimpla(String s).ıns˘
  • 96. Capitolul 4Intr˘ri ¸i ie¸iri a s s4.1 Introducere4.1.1 Ce sunt fluxurile?Majoritatea aplicatiilor necesit˘ citirea unor informatii care se g˘sesc pe ¸ a ¸ ao surs˘ extern˘ sau trimiterea unor informatii c˘tre o destinatie extern˘. a a ¸ a ¸ aInformatia se poate g˘si oriunde: ˆ ¸ a ıntr-un fi¸ier pe disc, ˆ retea, ˆ memorie s ın ¸ ınsau ˆ alt program ¸i poate fi de orice tip: date primitive, obiecte, imagini, ın ssunete, etc. Pentru a aduce informatii dintr-un mediu extern, un progam Java ¸trebuie s˘ deschid˘ un canal de comunicatie (flux) de la sursa informatiilor a a ¸ ¸(fi¸ier, memorie, socket, etc) ¸i s˘ citeasc˘ secvential informatiile respective. s s a a ¸ ¸ Similar, un program poate trimite informatii c˘tre o destinatie extern˘ ¸ a ¸ adeschizˆnd un canal de comunicatie (flux) c˘tre acea destinatie ¸i scriind a ¸ a ¸ ssecvential informatiile respective. ¸ ¸ Indiferent de tipul informatiilor, citirea/scrierea de pe/c˘tre un mediu ¸ aextern respect˘ urm˘torul algoritm: a a deschide canal comunicatie while (mai sunt informatii) { citeste/scrie informatie; } inchide canal comunicatie; Pentru a generaliza, atˆt sursa extern˘ a unor date cˆt ¸i destinatia lor a a a s ¸sunt v˘zute ca fiind ni¸te procese care produc, respectiv consum˘ informatii. a s a ¸ 95
  • 97. 96 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ Definitii: ¸Un flux este un canal de comunicatie unidirectional ˆ ¸ ¸ ıntre dou˘ procese. aUn proces care descrie o surs˘ extern˘ de date se nume¸te proces produc˘tor. a a s aUn proces care descrie o destinatie extern˘ pentru date se nume¸te proces ¸ a sconsumator.Un flux care cite¸te date se nume¸te flux de intrare. s sUn flux care scrie date se nume¸te flux de ie¸ire. s s Observatii: ¸Fluxurile sunt canale de comunicatie seriale pe 8 sau 16 biti. ¸ ¸Fluxurile sunt unidirectionale, de la produc˘tor la consumator. ¸ aFiecare flux are un singur proces produc˘tor ¸i un singur proces consumator. a sIntre dou˘ procese pot exista oricˆte fluxuri, orice proces putˆnd fi atˆt pro- a a a aducator cˆt ¸i consumator ˆ acela¸i timp, dar pe fluxuri diferite. a s ın sConsumatorul ¸i producatorul nu comunic˘ direct printr-o interfat˘ de flux s a ¸aci prin intermediul codului Java de tratare a fluxurilor. Clasele ¸i intefetele standard pentru lucrul cu fluxuri se g˘sesc ˆ pachetul s ¸ a ınjava.io. Deci, orice program care necesit˘ operatii de intrare sau ie¸ire tre- a ¸ sbuie s˘ contin˘ instructiunea de import a pachetului java.io: a ¸ a ¸ import java.io.*;4.1.2 Clasificarea fluxurilorExist˘ trei tipuri de clasificare a fluxurilor: a • Dup˘ directia canalului de comunicatie deschis fluxurile se ˆ a ¸ ¸ ımpart ˆ ın: – fluxuri de intrare (pentru citirea datelor) – fluxuri de ie¸ire (pentru scrierea datelor) s • Dup˘ tipul de date pe care opereaz˘: a a – fluxuri de octeti (comunicarea serial˘ se realizeaz˘ pe 8 biti) ¸ a a ¸ – fluxuri de caractere (comunicarea serial˘ se realizeaz˘ pe 16 biti) a a ¸
  • 98. 4.1. INTRODUCERE 97 • Dup˘ actiunea lor: a ¸ – fluxuri primare de citire/scriere a datelor (se ocup˘ efectiv cu a citirea/scrierea datelor) – fluxuri pentru procesarea datelor4.1.3 Ierarhia claselor pentru lucrul cu fluxuriClasele r˘d˘cin˘ pentru ierarhiile ce reprezint˘ fluxuri de caractere sunt: a a a a • Reader- pentru fluxuri de intrare ¸i s • Writer- pentru fluxuri de ie¸ire. sAcestea sunt superclase abstracte pentru toate clasele ce implementeaz˘ afluxuri specializate pentru citirea/scrierea datelor pe 16 biti ¸i vor contine ¸ s ¸metodele comune tuturor.Ca o regul˘ general˘, toate clasele din aceste ierarhii vor avea terminatia a a ¸Reader sau Writer ˆ functie de tipul lor, cum ar fi ˆ exemplele: FileReader, ın ¸ ınBufferedReader, FileWriter, BufferedWriter, etc. De asemenea, se ob-serv˘ ca o alt˘ regul˘ general˘, faptul c˘ unui flux de intrare XReader ˆ a a a a a ıicorespunde uzual un flux de ie¸ire XWriter, ˆ a acest lucru nu este obliga- s ıns˘toriu. Clasele radacin˘ pentru ierarhia fluxurilor de octeti sunt: a ¸ • InputStream- pentru fluxuri de intrare ¸i s • OutputStream- pentru fluxuri de ie¸ire. s Acestea sunt superclase abstracte pentru clase ce implementeaz˘ fluxuri aspecializate pentru citirea/scrierea datelor pe 8 biti. Ca ¸i ˆ cazul flux- ¸ s ınurilor pe caractere denumirile claselor vor avea terminatia superclasei lor: ¸FileInputStream, BufferedInputStream, FileOutputStream,BufferedOutputStream, etc., fiec˘rui flux de intrare XInputStream core- aspunzˆndu-i uzual un flux de ie¸ire XOutputStream, f˘r˘ ca acest lucru s˘ a s aa afie obligatoriu.
  • 99. 98 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ Pˆn˘ la un punct, exist˘ un paralelism ˆ a a a ıntre ierarhia claselor pentru flux-uri de caractere ¸i cea pentru fluxurile pe octeti. Pentru majoritatea pro- s ¸gramelor este recomandat ca scrierea ¸i citirea datelor s˘ se fac˘ prin inter- s a amediul fluxurilor de caractere, deoarece acestea permit manipularea carac-terelor Unicode ˆ timp ce fluxurile de octeti permit doar lucrul pe 8 biti - ın ¸caractere ASCII.4.1.4 Metode comune fluxurilorSuperclasele abstracte Reader ¸i InputStream definesc metode similare pen- stru citirea datelor. Reader InputStream int read() int read() int read(char buf[]) int read(byte buf[]) ... ... De asemenea, ambele clase pun la dispozitie metode pentru marcarea ¸unei locatii ˆ ¸ ıntr-un flux, saltul peste un num˘r de pozitii, resetarea pozitiei a ¸ ¸curente, etc. Acestea sunt ˆ a mai rar folosite ¸i nu vor fi detaliate. ıns˘ s Superclasele abstracte Writer ¸i OutputStream sunt de asemenea paralele, sdefinind metode similare pentru scrierea datelor: Reader InputStream void write(int c) void write(int c) void write(char buf[]) void write(byte buf[]) void write(String str) - ... ... Inchiderea oric˘rui flux se realizeaz˘ prin metoda close. In cazul ˆ care a a ınaceasta nu este apelat˘ explicit, fluxul va fi automat ˆ a ınchis de c˘tre colectorul ade gunoaie atunci cˆnd nu va mai exista nici o referint˘ la el, ˆ a acest lucru a ¸a ıns˘trebuie evitat deoarece, la lucrul cu fluxrui cu zon˘ tampon de memorie, adatele din memorie vor fi pierdute la ˆ ınchiderea fluxului de c˘tre gc. a Metodele referitoare la fluxuri pot genera exceptii de tipul IOException ¸sau derivate din aceast˘ clas˘, tratarea lor fiind obligatorie. a a
  • 100. 4.2. FOLOSIREA FLUXURILOR 994.2 Folosirea fluxurilorA¸a cum am v˘zut, fluxurile pot fi ˆ artite ˆ functie de activitatea lor s a ımp˘ ¸ ın ¸ˆ fluxuri care se ocup˘ efectiv cu citirea/scrierea datelor ¸i fluxuri pentruın a sprocesarea datelor (de filtrare). In continuare, vom vedea care sunt cele maiimportante clase din cele dou˘ categorii ¸i la ce folosesc acestea, precum ¸i a s smodalit˘¸ile de creare ¸i utilizare a fluxurilor. at s4.2.1 Fluxuri primitiveFluxurile primitive sunt responsabile cu citirea/scrierea efectiv˘ a datelor, apunˆnd la dispozitie implement˘ri ale metodelor de baz˘ read, respectiv a ¸ a awrite, definite ˆ superclase. In functie de tipul sursei datelor, ele pot fi ın ¸ˆ artite astfel:ımp˘ ¸ • Fi¸ier s FileReader, FileWriter FileInputStream, FileOutputStream Numite ¸i fluxuri fi¸ier, acestea sunt folosite pentru citirea datelor s s dintr-un fi¸ier, respectiv scrierea datelor ˆ s ıntr-un fi¸ier ¸i vor fi analizate s s ˆ ıntr-o sectiune separat˘ (vezi ”Fluxuri pentru lucrul cu fi¸iere”). ¸ a s • Memorie CharArrayReader, CharArrayWriter ByteArrayInputStream, ByteArrayOutputStream Aceste fluxuri folosesc pentru scrierea/citirea informatiilor ˆ ¸ ın/din mem- orie ¸i sunt create pe un vector existent deja. Cu alte cuvinte, permit s tratarea vectorilor ca surs˘/destinatie pentru crearea unor fluxuri de a ¸ intrare/ie¸ire. s StringReader, StringWriter Permit tratarea ¸irurilor de caractere aflate ˆ memorie ca surs˘/destinatie s ın a ¸ pentru crearea de fluxuri. • Pipe PipedReader, PipedWriter PipedInputStream, PipedOutputStream Implementeaz˘ componentele de intrare/ie¸ire ale unei conducte de a s
  • 101. 100 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ date (pipe). Pipe-urile sunt folosite pentru a canaliza ie¸irea unui pro- s gram sau fir de executie c˘tre intrarea altui program sau fir de executie. ¸ a ¸4.2.2 Fluxuri de procesareFluxurile de procesare (sau de filtrare) sunt responsabile cu preluarea datelorde la un flux primitiv ¸i procesarea acestora pentru a le oferi ˆ s ıntr-o alt˘ form˘, a amai util˘ dintr-un anumit punct de vedere. De exemplu, BufferedReader apoate prelua date de la un flux FileReader ¸i s˘ ofere informatia dintr-un s a ¸fi¸ier linie cu linie. Fiind primitiv, FileReader nu putea citi decˆt caracter s acu caracter. Un flux de procesare nu poate fi folosit decˆt ˆ a ımpreun˘ cu un aflux primitiv. Clasele ce descriu aceste fluxuri pot fi ˆ ımpartite ˆ functie de tipul de ın ¸procesare pe care ˆ efectueaza astfel: ıl • ”Bufferizare” BufferedReader, BufferedWriter BufferedInputStream, BufferedOutputStream Sunt folosite pentru a introduce un buffer ˆ procesul de citire/scriere ın a informatiilor, reducˆnd astfel num˘rul de acces˘ri la dispozitivul ce ¸ a a a reprezint˘ sursa/destinatia original˘ a datelor. Sunt mult mai eficiente a ¸ a decˆt fluxurile f˘r˘ buffer ¸i din acest motiv se recomand˘ folosirea lor a aa s a ori de cˆte ori este posibil (vezi ”Citirea ¸i scrierea cu zona tampon”). a s • Filtrare FilterReader, FilterWriter FilterInputStream, FilterOutputStream Sunt clase abstracte ce definesc o interfat˘ comun˘ pentru fluxuri care ¸a a filtreaz˘ automat datele citite sau scrise (vezi ”Fluxuri pentru filtrare”). a • Conversie octeti-caractere ¸ InputStreamReader, OutputStreamWriter Formeaz˘ o punte de legatur˘ ˆ a a ıntre fluxurile de caractere ¸i fluxurile s de octeti. Un flux InputStreamReader cite¸te octeti dintr-un flux ¸ s ¸ InputStream ¸i ˆ converte¸te la caractere, folosind codificarea stan- s ıi s dard a caracterelor sau o codificare specificat˘ de program. Similar, a un flux OutputStreamWriter converte¸te caractere ˆ octeti ¸i trimite s ın ¸ s rezutatul c˘tre un flux de tipul OutputStream. a
  • 102. 4.2. FOLOSIREA FLUXURILOR 101 • Concatenare SequenceInputStream Concateneaz˘ mai multe fluxuri de intrare ˆ a ıntr-unul singur (vezi ”Con- catenarea fi¸ierelor”). s • Serializare ObjectInputStream, ObjectOutputStream Sunt folosite pentru serializarea obiectelor (vezi ”Serializarea obiectelor”). • Conversie tipuri de date DataInputStream, DataOutputStream Folosite la scrierea/citirea datelor de tip primitiv ˆ ıntr-un format binar, independent de ma¸ina pe care se lucreaz˘ (vezi ”Folosirea claselor s a DataInputStream ¸i DataOutputStream”). s • Num˘rare a LineNumberReader LineNumberInputStream Ofer˘ ¸i posibilitatea de num˘rare automat˘ a liniilor citite de la un as a a flux de intrare. • Citire ˆ avans ın PushbackReader PushbackInputStream Sunt fluxuri de intrare care au un buffer de 1-caracter(octet) ˆ care ın este citit ˆ avans ¸i caracterul (octetul) care urmeaz˘ celui curent citit. ın s a • Afi¸are s PrintWriter PrintStream Ofer˘ metode convenabile pentru afisarea informatiilor. a ¸4.2.3 Crearea unui fluxOrice flux este un obiect al clasei ce implementeaz˘ fluxul respectiv. Crearea aunui flux se realizeaz˘ a¸adar similar cu crearea obiectelor, prin instructiunea a s ¸new ¸i invocarea unui constructor corespunz˘tor al clasei respective: s a Exemple:
  • 103. 102 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ //crearea unui flux de intrare pe caractere FileReader in = new FileReader("fisier.txt"); //crearea unui flux de iesire pe caractere FileWriter out = new FileWriter("fisier.txt"); //crearea unui flux de intrare pe octeti FileInputStream in = new FileInputStream("fisier.dat"); //crearea unui flux de iesire pe octeti FileOutputStrem out = new FileOutputStream("fisier.dat"); A¸adar, crearea unui flux primitiv de date care cite¸te/scrie informatii de s s ¸la un dispozitiv extern are formatul general: FluxPrimitiv numeFlux = new FluxPrimitiv(dispozitivExtern); Fluxurile de procesare nu pot exista de sine st˘t˘toare ci se suprapun pe aaun flux primitiv de citire/scriere a datelor. Din acest motiv, constructoriiclaselor pentru fluxurile de procesare nu primesc ca argument un dispozitivextern de memorare a datelor ci o referinta la un flux primitiv responsabil ¸cu citirea/scrierea efectiv˘ a datelor: aExemple: //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¸adar, crearea unui flux pentru procesarea datelor are formatul general: s
  • 104. 4.2. FOLOSIREA FLUXURILOR 103 FluxProcesare numeFlux = new FluxProcesare(fluxPrimitiv); In general, fluxurile pot fi compuse ˆ succesiuni oricˆt de lungi: ın a DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("fisier.dat")));4.2.4 Fluxuri pentru lucrul cu fi¸iere sFluxurile pentru lucrul cu fi¸iere sunt cele mai usor de ˆ s ınteles, ˆ ıntrucˆt aoperatia lor de baz˘ este citirea, respectiv scrierea unui caracter sau octet ¸ adintr-un sau ˆıntr-un fi¸ier specificat uzual prin numele s˘u complet sau relativ s ala directorul curent. Dup˘ cum am v˘zut deja, clasele care implementeaz˘ aceste fluxuri sunt a a aurm˘toarele: a FileReader, FileWriter - caractere FileInputStream, FileOutputStream - octeti Constructorii acestor clase accept˘ ca argument un obiect care s˘ specifice a aun anume fi¸ier. Acesta poate fi un ¸ir de caractere, on obiect de tip File s ssau 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 append) throws IOException
  • 105. 104 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ Cei mai uzuali constructori sunt cei care primesc ca argument numelefi¸ierului. Ace¸tia pot provoca exceptii de tipul FileNotFoundException ˆ s s ¸ ıncazul ˆ care fi¸ierul cu numele specificat nu exist˘. Din acest motiv orice ın s acreare a unui flux de acest tip trebuie f˘cut˘ ˆ a a ıntr-un bloc try-catch saumetoda ˆ care sunt create fluxurile respective trebuie s˘ arunce exceptiile ın a ¸de tipul FileNotFoundException sau de tipul superclasei IOException. S˘ consider˘m ca exemplu un program care copie continutul unui fi¸ier a a ¸ scu numele ”in.txt” ˆ ıntr-un alt fi¸ier cu numele ”out.txt”. Ambele fi¸iere sunt s sconsiderate ˆ directorul curent. ın Listing 4.1: Copierea unui fisierimport java . io .*;public class Copiere { public static void main ( String [] args ) { try { FileReader in = new FileReader ( " in . txt " ) ; FileWriter out = new FileWriter ( " out . txt " ) ; int c ; while (( c = in . read () ) != -1) out . write ( c ) ; in . close () ; out . close () ; } catch ( IOException e ) { System . err . println ( " Eroare la operatiile cu fisiere ! " ) ; e . printStackTrace () ; } }} In cazul ˆ care vom lansa aplicatia iar ˆ directorul curent nu exist˘ un ın ¸ ın afi¸ier cu numele ”in.txt”, va fi generat˘ o exceptie de tipul s a ¸FileNotFoundException. Aceasta va fi prins˘ de program deoarece, IOException aeste superclas˘ pentru FileNotFoundException. aDac˘ exist˘ fi¸ierul ”in.txt”, aplicatia va crea un nou fi¸ier ”out.txt” ˆ care a a s ¸ s ınva fi copiat continutul primului. Dac˘ exist˘ deja fi¸ierul ”out.txt” el va fi re- ¸ a a s
  • 106. 4.2. FOLOSIREA FLUXURILOR 105scris. Dac˘ doream s˘ facem operatia de ad˘ugare(append) ¸i nu de rescriere a a ¸ a spentru fi¸ierul ”out.txt” foloseam: s FileWriter out = new FileWriter("out.txt", true);4.2.5 Citirea ¸i scrierea cu buffer sClasele pentru citirea/scrierea cu zona tampon sunt: BufferedReader, BufferedWriter - caractere BufferedInputStream, BufferedOutputStream - octeti Sunt folosite pentru a introduce un buffer (zon˘ de memorie) ˆ proce- a ınsul de citire/scriere a informatiilor, reducˆnd astfel numarul de acces˘ri ale ¸ a adispozitivului ce reprezint˘ sursa/destinatia atelor. Din acest motiv, sunt a ¸mult mai eficiente decˆt fluxurile f˘r˘ buffer ¸i din acest motiv se recomand˘ a aa s afolosirea lor ori de cˆte ori este posibil. a Clasa BufferedReader cite¸te ˆ avans date ¸i le memoreaz˘ ˆ s ın s a ıntr-o zon˘atampon. Atunci cˆnd se execut˘ o operatie de citire, caracterul va fi pre- a a ¸luat din buffer. In cazul ˆ care buffer-ul este gol, citirea se face direct din ınflux ¸i, odat˘ cu citirea caracterului, vor fi memorati ˆ buffer ¸i caracterele s a ın scare ˆ urmeaz˘. Evident, BufferedInputStream functioneaz˘ dup˘ acela¸i ıi a ¸ a a sprincipiu, singura diferent˘ fiind faptul c˘ sunt cititi octeti. ¸a a ¸ ¸ Similar lucreaza ¸i clasele BufferedWriter ¸i BufferedOutputStream. s sLa operatiile de scriere datele scrise nu vor ajunge direct la destinatie, ci vor ¸ ¸fi memorate jntr-un buffer de o anumit˘ dimensiune. Atunci cˆnd bufferul a aeste plin, continutul acestuia va fi transferat automat la destinatie. ¸ ¸ Fluxurile de citire/scriere cu buffer sunt fluxuri de procesare ¸i sunt sfolosite prin suprapunere cu alte fluxuri, dintre care obligatoriu unul esteprimitiv. BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream("out.dat"), 1024) //1024 este dimensiunea bufferului Constructorii cei mai folositi ai acestor clase sunt urm˘torii: ¸ a BufferedReader(Reader in) BufferedReader(Reader in, int dim_buffer) BufferedWriter(Writer out)
  • 107. 106 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ BufferedWriter(Writer out, int dim_buffer) BufferedInputStream(InputStream in) BufferedInputStream(InputStream in, int dim_buffer) BufferedOutputStream(OutputStream out) BufferedOutputStream(OutputStream out, int dim_buffer) In cazul constructorilor ˆ care dimensiunea buffer-ului nu este specificat˘, ın aaceasta prime¸te valoarea implicit˘ de 512 octeti (caractere). s a ¸ Metodele acestor clase sunt cele uzuale de tipul read ¸i write. Pe lˆnga s aacestea, clasele pentru scriere prin buffer mai au ¸i metoda flush care gole¸te s sexplicit zona tampon, chiar dac˘ aceasta nu este plin˘. a a BufferedWriter out = new BufferedWriter( new FileWriter("out.dat"), 1024) //am creat un flux cu buffer de 1024 octeti for(int i=0; i<1000; i++) out.write(i); //bufferul nu este plin, in fisier nu s-a scris nimic out.flush(); //bufferul este golit, datele se scriu in fisier Metoda readLineEste specific˘ fluxurilor de citire cu buffer ¸i permite citirea linie cu linie a a sdatelor de intrare. O linie reprezint˘ o succesiune de caractere terminat˘ cu a asimbolul pentru sfˆr¸it de linie, dependent de platforma de lucru. Acesta aseste reprezentat ˆ Java prin secventa escape ’n’; ın ¸ BufferedReader br = new BufferedReader(new FileReader("in")) String linie; while ((linie = br.readLine()) != null) { ... //proceseaza linie } br.close();}
  • 108. 4.2. FOLOSIREA FLUXURILOR 1074.2.6 Concatenarea fluxurilorClasa SequenceInputStream permite unei aplicatii s˘ combine serial mai amulte fluxuri de intrare astfel ˆ at acestea s˘ apar˘ ca un singur flux de ıncˆ a aintrare. Citirea datelor dintr-un astfel de flux se face astfel: se cite¸te din sprimul flux de intrare specificat pˆna cˆnd se ajunge la sfˆrsitul acestuia, a a adup˘ care primul flux de intrare este ˆ a ınchis ¸i se deschide automat urm˘torul s aflux de intrare din care se vor citi ˆ continuare datele, dup˘ care procesul se ın arepet˘ pˆna la terminarea tuturor fluxurilor de intrare. a a Constructorii acestei clase sunt: SequenceInputStream(Enumeration e) SequenceInputStream(InputStream s1, InputStream s2) Primul construieste un flux secvential dintr-o multime de fluxuri de in- ¸trare. Fiecare obiect ˆ enumerarea primit˘ ca parametru trebuie s˘ fie de ın a atipul InputStream.Cel de-al doilea construie¸te un flux de intrare care combin˘ doar dou˘ fluxuri s a as1 ¸i s2, primul flux citit fiind s1. s Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea adou˘ sau mai multor fi¸iere: a s Listing 4.2: Concatenarea a dou˘ fi¸iere a s/* Concatenarea a doua fisiere ale caror nume sunt primite de la linia de comanda . Rezultatul concatenarii este afisat pe ecran .*/import java . io .*;public class Concatenare { public static void main ( String args []) { if ( args . length <= 1) { System . out . println ( " Argumente insuficiente ! " ) ; System . exit ( -1) ; } try { FileInputStream f1 = new FileInputStream ( args [0]) ; FileInputStream f2 = new FileInputStream ( args [1]) ; SequenceInputStream s = new S eq u e nc e I np u tS t r ea m ( f1 , f2 ) ; int c ; while (( c = s . read () ) != -1) System . out . print (( char ) c ) ;
  • 109. 108 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ s . close () ; // f1 si f2 sunt inchise automat } catch ( IOException e ) { e . printStackTrace () ; } }} Pentru concatenarea mai multor fi¸iere exist˘ dou˘ variante: s a a • folosirea unei enumer˘ri - primul constructor (vezi ”Colectii”); a ¸ • concatenarea pe rˆnd a acestora folosind al 2-lea constructor; con- a catenarea a 3 fi¸iere va construi un flux de intrare astfel: s FileInputStream f1 = new FileInputStream(args[0]); FileInputStream f2 = new FileInputStream(args[1]); FileInputStream f3 = new FileInputStream(args[2]); SequenceInputStream s = new SequenceInputStream( f1, new SequenceInputStream(f2, f3));4.2.7 Fluxuri pentru filtrarea datelorUn flux de filtrare se ata¸eaz˘ altui flux pentru a filtra datele care sunt s acitite/scrise de c˘tre acel flux. Clasele pentru filtrarea datelor superclasele aabstracte: • FilterInputStream - pentru filtrarea fluxurilor de intrare ¸i s • FilterOutputStream - pentru filtrarea fluxurilor de ie¸ire. s Cele mai importante fluxruri pentru filtrarea datelor sunt implementatede clasele: DataInputStream, DataOutputStream BufferedInputStream, BufferedOutputStream LineNumberInputStream PushbackInputStream PrintStream
  • 110. 4.2. FOLOSIREA FLUXURILOR 109 Observati c˘ toate aceste clase descriu fluxuri de octeti. ¸ a ¸ Filtrarea datelor nu trebuie v˘zut˘ ca o metod˘ de a elimina anumiti a a a ¸octeti dintr-un flux ci de a transforma ace¸ti octeti ˆ date care s˘ poat˘ fi s ¸ ın a ainterpretate sub alt˘ form˘. A¸a cum am v˘zut la citirea/scrierea cu zon˘ a a s a atampon, clasele de filtrare BufferedInputStream ¸i BufferedOutputStream scolecteaz˘ datele unui flux ˆ a ıntr-un buffer, urmˆnd ca citirea/scrierea s˘ se a afac˘ prin intermediu acelui buffer. a A¸adar, fluxurile de filtrare nu elimin˘ date citite sau scrise de un anumit s aflux, ci introduc o noua modalitate de manipulare a lor, ele mai fiind numite¸i fluxuri de procesare. Din acest motiv, fluxurile de filtrare vor contines ¸anumite metode specializate pentru citirea/scrierea datelor, altele decˆt cele acomune tuturor fluxurilor. De exemplu, clasa BufferedInputStream punela dispozitie metoda readLine pentru citirea unei linii din fluxul de intrare. ¸ Folosirea fluxurilor de filtrare se face prin ata¸area lor de un flux care se socup˘ efectiv de citirea/scrierea datelor: aFluxFiltrare numeFlux = new FluxFiltrare(referintaAltFlux);4.2.8 Clasele DataInputStream ¸i DataOutputStream sAceste clase ofer˘ metode prin care un flux nu mai este v˘zut ca o ˆ a a ınsiruirede octeti, ci de date primitive. Prin urmare, vor furniza metode pentru ¸citirea ¸i scrierea datelor la nivel de tip primitiv ¸i nu la nivel de octet. s sClasele care ofer˘ un astfel de suport implementeaz˘ interfetele DataInput, a a ¸respectiv DataOutput. Acestea definesc metodele pe care trebuie s˘ le pun˘ la a adispozitie ˆ vederea citireii/scrierii datelor de tip primitiv. Cele mai folosite ¸ ınmetode, altele decˆt cele comune tuturor fluxurilor, sunt date ˆ tabelul de a ınmai jos:
  • 111. 110 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ DataInputStream DataOutputStream readBoolean writeBoolean readByte writeByte readChar writeChar readDouble writeDouble readFloat writeFloat readInt writeInt readLong writeLong readShort writeShort readUTF writeUTF Aceste metode au denumirile generice de readXXX ¸i writeXXX, specifi- scate de interfetele DataInput ¸i DataOutput ¸i pot provoca exceptii de tipul s s ¸IOException. Denumirile lor sunt sugestive pentru tipul de date pe care ˆ ılprelucreaz˘. mai putin readUTF ¸i writeUTF care se ocup˘ cu obiecte de tip a ¸ s aString, fiind singurul tip referint˘ permis de aceste clase. ¸a Scrierea datelor folosind fluxuri de acest tip se face ˆ format binar, ceea ınce ˆınseamn˘ c˘ un fi¸ier ˆ care au fost scrise informatii folosind metode a a s ın ¸writeXXX nu va putea fi citit decˆt prin metode readXXX. a Transformarea unei valori ˆ format binar se nume¸te serializare. Clasele ın sDataInputStream ¸i DataOutputStream permit serializarea tipurilor prim- sitive ¸i a ¸irurilor de caractere. Serializarea celorlalte tipuri referint˘ va fi s s ¸af˘cut˘ prin intermediul altor clase, cum ar fi ObjectInputStream ¸i a a sObjectOutputStream (vezi ”Serializarea obiectelor”).4.3 Intr˘ri ¸i ie¸iri formatate a s sIncepˆnd cu versiunea 1.5, limbajul Java pune la dispozitii modalit˘¸i sim- a ¸ atplificate pentru afi¸area formatat˘ a unor informatii, respectiv pentru citirea s a ¸de date formatate de la tastatur˘. a4.3.1 Intr˘ri formatate aClasa java.util.Scanner ofer˘ o solutie simpl˘ pentru formatarea unor informatii a ¸ a ¸citite de pe un flux de intrare fie pe octeti, fie pe caractere, sau chiar dintr-un ¸obiect de tip File. Pentru a citi de la tastatur˘ vom specifica ca argument aal constructorului fluxul System.in:
  • 112. 4.4. FLUXURI STANDARD DE INTRARE SI IESIRE ¸ ¸ 111 Scanner s = Scanner.create(System.in); String nume = s.next(); int varsta = s.nextInt(); double salariu = s.nextDouble(); s.close();4.3.2 Ie¸iri formatate sClasele PrintStream ¸i PrintWriter pun la dispozitiile, pe lˆng˘ metodele s ¸ a aprint, println care ofereau posibilitatea de a afi¸a un ¸ir de caractere, ¸i s s smetodele format, printf (echivalente) ce permit afi¸area formatat˘ a unor s avariabile. System.out.printf("%s %8.2f %2d %n", nume, salariu, varsta);Formatarea ¸irurilor de caractere se bazeaz˘ pe clasa java.util.Formatter. s a4.4 Fluxuri standard de intrare ¸i ie¸ire s sMergˆnd pe linia introdus˘ de sistemul de operare UNIX, orice program Java a aare : • o intrare standard • o ie¸ire standard s • o ie¸ire standard pentru erori s In general, intrarea standard este tastatura iar ie¸irea standard este ecranul. s Intrarea ¸i ie¸irea standard sunt reprezentate de obiecte pre-create ce s sdescriu fluxuri de date care comunic˘ cu dispozitivele standard ale sistemului. aAceste obiecte sunt definite publice ˆ clasa System ¸i sunt: ın s • System.in - fluxul standar de intrare, de tip InputStream • System.out - fluxul standar de ie¸ire, de tip PrintStream s • System.err - fluxul standar pentru erori, de tip PrintStream
  • 113. 112 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸4.4.1 Afisarea informatiilor pe ecran ¸Am v˘zut deja numeroase exemple de utilizare a fluxului standard de ie¸ire, a sel fiind folosit la afi¸area oric˘ror rezultate pe ecran (ˆ modul consola): s a ın System.out.print (argument); System.out.println(argument); System.out.printf (format, argumente...); System.out.format (format, argumente...); Fluxul standard pentru afi¸area erorilor se folose¸te similar ¸i apare uzual s s sˆ secventele de tratare a exceptiilor. Implicit, este acela¸i cu fluxul standardın ¸ ¸ sde ie¸ire. s catch(Exception e) { System.err.println("Exceptie:" + e); }Fluxurile de ie¸ire pot fi folosite a¸adar f˘r˘ probleme deoarece tipul lor s s aaeste PrintStream, clas˘ concret˘ pentru scrierea datelor. In schimb, fluxul a astandard de intrare System.out este de tip InputStream, care este o clas˘ aabstract˘, deci pentru a-l putea utiliza eficient va trebui sa-l folosim ˆ a ımpreunacu un flux de procesare(filtrare) care s˘ permit˘ citirea facil˘ a datelor. a a a4.4.2 Citirea datelor de la tastatur˘ aUzual, vom dori s˘ folosim metoda readLine pentru citirea datelor de la atastatura ¸i din acest motiv vom folosi intrarea standard ˆ s ımpreun˘ cu o clas˘ a ade procesare care ofer˘ aceast˘ metod˘. Exemplul tipic este: a a a BufferedReader stdin = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Introduceti o linie:"); String linie = stdin.readLine() System.out.println(linie); In exemplul urm˘tor este prezentat un program care afi¸eaza liniile intro- a sduse de la tastatur˘ pˆn˘ ˆ momentul ˆ care se introduce linia ”exit” sau a a a ın ıno linie vid˘ ¸i mentioneaz˘dac˘ ¸irul respectiv reprezint˘ un num˘r sau nu. as ¸ a as a a
  • 114. 4.4. FLUXURI STANDARD DE INTRARE SI IESIRE ¸ ¸ 113 Listing 4.3: Citirea datelor de la tastatur˘ a/* Citeste siruri de la tastatura si verifica daca reprezinta numere sau nu*/import java . io .*;public class EsteNumar {public static void main ( String [] args ) { BufferedReader stdin = new BufferedReader ( new InputStreamReader ( System . in ) ) ; try { while ( true ) { String s = stdin . readLine () ; if ( s . equals ( " exit " ) || s . length () ==0) break ; System . out . print ( s ) ; try { Double . parseDouble ( s ) ; System . out . println ( " : DA " ) ; } catch ( Numb erFor ma t E x c e p t i o n e ) { System . out . println ( " : NU " ) ; } } } catch ( IOException e ) { System . err . println ( " Eroare la intrarea standard ! " ) ; e . printStackTrace () ; } }} Incepˆnd cu versiunea 1.5, varianta cea mai comod˘ de citire a datelor a ade la tastatur˘ este folosirea clasei java.util.Scanner. a4.4.3 Redirectarea fluxurilor standardRedirectarea fluxurilor standard presupune stabilirea unei alte surse decˆt atastatura pentru citirea datelor, respectiv alte destinatii decˆt ecranul pentru ¸ acele dou˘ fluxuri de ie¸ire. In clasa System exist˘ urm˘toarele metode statice a s a acare realizeaz˘ acest lucru: a setIn(InputStream) - redirectare intrare setOut(PrintStream) - redirectare iesire setErr(PrintStream) - redirectare erori
  • 115. 114 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸Redirectarea ie¸irii este util˘ ˆ special atunci cˆnd sunt afi¸ate foarte multe s a ın a sdate pe ecran. Putem redirecta afisarea c˘tre un fi¸ier pe care s˘-l citim dup˘ a s a aexecutia programului. Secventa clasic˘ de redirectare a ie¸irii este c˘tre un ¸ ¸ a s afi¸ier este: s PrintStream fis = new PrintStream( new FileOutputStream("rezultate.txt"))); System.setOut(fis);Redirectarea erorilor ˆ ıntr-un fi¸ier poate fi de asemenea util˘ ¸i se face ˆ s as ıntr-omanier˘ similar˘: a a PrintStream fis = new PrintStream( new FileOutputStream("erori.txt"))); System.setErr(fis); Redirectarea intr˘rii poate fi folositoare pentru un program ˆ mod con- a ınsol˘ care prime¸te mai multe valori de intrare. Pentru a nu le scrie de la a stastatur˘ de fiecare dat˘ ˆ timpul test˘rii programului, ele pot fi puse ˆ a a ın a ıntr-un fi¸ier, redirectˆnd intrarea standard c˘tre acel fi¸ier. In momentul cˆnd s a a s atestarea programului a luat sfˆrsit redirectarea poate fi eliminat˘, datele fiind a acerute din nou de la tastatur˘. a Listing 4.4: Exemplu de folosire a redirect˘rii: aimport java . io .*;class Redirectare { public static void main ( String [] args ) { try { BufferedInputS t r ea m in = new B uf f e re d I np u tS t r ea m ( new FileInputStream ( " intrare . txt " ) ) ; PrintStream out = new PrintStream ( new FileOutputStream ( " rezultate . txt " ) ) ; PrintStream err = new PrintStream ( new FileOutputStream ( " erori . txt " ) ) ; System . setIn ( in ) ; System . setOut ( out ) ; System . setErr ( err ) ; BufferedReader br = new BufferedReader ( new InputStream Reader ( System . in ) ) ;
  • 116. 4.4. FLUXURI STANDARD DE INTRARE SI IESIRE ¸ ¸ 115 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 () ; } }}4.4.4 Analiza lexical˘ pe fluxuri (clasa StreamTokenizer) aClasa StreamTokenizer proceseaz˘ un flux de intrare de orice tip ¸i ˆ ˆ a s ıl ımparteˆ ”atomi lexicali”. Rezultatul va consta ˆ faptul c˘ ˆ loc s˘ se citeasc˘ın ın a ın a aocteti sau caractere, se vor citi, pe rˆnd, atomii lexicali ai fluxului respectiv. ¸ aPrintr-un atom lexical se ˆın]elege ˆ general: ın • un identificator (un ¸ir care nu este ˆ s ıntre ghilimele) • un numar • un ¸ir de caractere s • un comentariu • un separatorAtomii lexicali sunt desp˘rtiti ˆ a ¸ ıntre ei de separatori. Implicit, ace¸ti separa- stori sunt cei obi¸nuti: spatiu, tab, virgul˘, punct ¸i virgula, etc., ˆ a pot fi s ¸ ¸ a s ıns˘schimbati prin diverse metode ale clasei. ¸ Constructorii clasei sunt:
  • 117. 116 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ public StreamTokenizer(Reader r) public StreamTokenizer(InputStream is) Identificarea tipului ¸i valorii unui atom lexical se face prin intermediul svariabilelor: • TT EOF - atom ce marcheaz˘ sfˆar¸itul fluxului a a s • TT EOL - atom ce marcheaz˘ sfˆr¸itul unei linii a as • TT NUMBER - atom de tip num˘r a • TT WORD- atom de tip cuvˆnt a • ttype- tipul ultimului atom citit din flux • nval- valoarea unui atom numeric • sval - valoarea unui atom de tip cuvˆnt a Citirea atomilor din flux se face cu metoda nextToken(), care returnez˘ atipul atomului lexical citit ¸i scrie ˆ variabilele nval sau sval valoarea core- s ınspunzato˘re atomului. a Exemplul tipic de folosire a unui analizor lexical este citirea unei secvente ¸de numere ¸i ¸iruri aflate ˆ s s ıntr-un fi¸ier sau primite de la tastatur˘: s a Listing 4.5: Citirea unor atomi lexicali dintr-un fisier/* 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
  • 118. 4.5. CLASA RANDOMACCESFILE (FISIERE CU ACCES DIRECT) ¸ 117 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 } }} A¸adar, modul de utilizare tipic pentru un analizor lexical este ˆ s ıntr-o bu-cla ”while”, ˆ care se citesc atomii unul cˆte unul cu metoda nextToken, ın apˆna se ajunge la sfˆrsitul fluxului (TT EOF). In cadrul buclei ”while” se deter- a amin˘ tipul atomul curent curent (ˆ a ıntors de metoda nextToken) ¸i apoi se afl˘ s avaloarea numeric˘ sau ¸irul de caractere corespunz˘tor atomului respectiv. a s a In cazul ˆ care tipul atomilor nu ne intereseaz˘, este mai simplu s˘ ın a acitim fluxul linie cu linie ¸i s˘ folosim clasa StringTokenizer, care realizeaz˘ s a aˆ artirea unui ¸ir de caractere ˆ atomi lexicali, sau metoda split a claseiımp˘ ¸ s ınString.4.5 Clasa RandomAccesFile (fi¸iere cu acces di- s rect)Dup˘ cum am v˘zut, fluxurile sunt procese secventiale de intrare/ie¸ire. a a ¸ sAcestea sunt adecvate pentru lucrul cu medii secventiale de memorare a ¸datelor, cum ar fi banda magnetic˘ sau pentru transmiterea informatiilor a ¸prin retea, desi sunt foarte utile ¸i pentru dispozitive ˆ care informatia poate ¸ s ın ¸fi accesat˘ direct. a Clasa RandomAccesFile are urm˘toarele caracteristici: a • permite accesul nesecvential (direct) la continutul unui fi¸ier; ¸ ¸ s • este o clas˘ de sine st˘t˘toare, subclas˘ direct˘ a clasei Object; a aa a a • se g˘se¸te ˆ pachetul java.io; a s ın
  • 119. 118 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ • implementeaz˘ interfetele DataInput ¸i DataOutput, ceea ce ˆ a ¸ s ınseamna ca sunt disponibile metode de tipul readXXX, writeXXX, ˆ ıntocmai ca la clasele DataInputStream ¸i DataOutputStream; s • permite atˆt citirea cˆt ¸i scriere din/in fi¸iere cu acces direct; a a s s • permite specificarea modului de acces al unui fi¸ier (read-only, read- s write). Constructorii acestei clase sunt:RandomAccessFile(StringnumeFisier, StringmodAcces) throws IOExceptionRandomAccessFile(StringnumeFisier, StringmodAcces) throws IOExceptionunde modAcces poate fi: • ”r” - fi¸ierul este deschis numai pentru citire (read-only) s • ”rw” - fi¸ierul este deschis pentru citire ¸i scriere (read-write) s sExemple: RandomAccesFile f1 = new RandomAccessFile("fisier.txt", "r"); //deschide un fisier pentru citire RandomAccesFile f2 = new RandomAccessFile("fisier.txt", "rw"); //deschide un fisier pentru scriere si citire Clasa RandomAccesFile suport˘ notiunea de pointer de fi¸ier. Acesta este a ¸ sun indicator ce specific˘ pozitia curent˘ ˆ fi¸ier. La deschiderea unui fi¸ier a ¸ a ın s spointerul are valoarea 0, indicˆnd ˆ a ınceputul fi¸ierului. Apeluri la metodele sde citire/scrirere deplaseaz˘ pointerul fi¸ierului cu num˘rul de octeti cititi a s a ¸ ¸sau scri¸i de metodele respective. s In plus fat˘de metodele de tip read ¸i write clasa pune la dispozitie ¸i ¸a s smetode pentru controlul pozitiei pointerului de fi¸ier. Acestea sunt: ¸ s • skipBytes - mut˘ pointerul fi¸ierului ˆ a s ınainte cu un num˘r specificat de a octeti ¸ • seek - pozitioneaza pointerului fi¸ierului ˆ ¸ s ınaintea unui octet specificat • getFilePointer - returneaz˘ pozitia pointerului de fi¸ier. a ¸ s
  • 120. 4.6. CLASA FILE 1194.6 Clasa FileClasa File nu se refer˘ doar la un fi¸ier ci poate reprezenta fie un fi¸ier a s sanume, fie multimea fi¸ierelor dintr-un director. s Specificarea unui fi¸ier/director se face prin specificarea c˘ii absolute spre s aacel fi¸ier sau a c˘ii relative fat˘ de directorul curent. Acestea trebuie s˘ re- s a ¸a aspecte conventiile de specificare a c˘ilor ¸i numelor fi¸ierelor de pe platforma ¸ a s sde lucru.Utilitate clasei File const˘ ˆ furnizarea unei modalit˘¸i de a abstractiza a ın atdependentele cailor ¸i numelor fi¸ierelor fat˘de ma¸ina gazd˘, precum ¸i ¸ s s ¸a s a spunerea la dispozitie a unor metode pentru lucrul cu fisere ¸i directoare la ¸ snivelul sistemului de operare. Astfel, ˆ aceast˘ clas˘ vom g˘si metode pentru testarea existentei, ¸tergerea, ın a a a ¸ sredenumirea unui fi¸ier sau director, crearea unui director, listarea fi¸ierelor s sdintr-un director, etc. Trebuie mentionat ¸i faptul c˘ majoritatea constructorilor fluxurilor care ¸ s apermit accesul la fi¸iere accept˘ ca argument un obiect de tip File ˆ locul s a ınunui ¸ir ce reprezint˘ numele fi¸ierului respectiv. s a s File f = new File("fisier.txt"); FileInputStream in = new FileInputStream(f) Cel mai uzual constructor al clasei File este: public File(String numeFisier) Metodele mai importante ale clasei File au denumiri sugestive ¸i vor fi sprezentate prin intermediul exemplului urm˘tor care listeaz˘ fi¸ierele ¸i sub- a a s sdirectoarele unui director specificat ¸i, pentru fiecare din ele afi¸eaz˘ diverse s s ainformatii: ¸ Listing 4.6: Listarea continutului unui director ¸/* Programul listeaza fisierele si subdirectoarele unui director . Pentru fiecare din ele vor fi afisate diverse informatii . Numele directorului este primit ca argument de la linia de comanda , sau este directorul curent .*/import java . io .*;
  • 121. 120 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸import java . util .*;public class ListareDirector { 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 () ; } }}
  • 122. Capitolul 5Interfete ¸5.1 Introducere5.1.1 Ce este o interfat˘ ? ¸aInterfetele duc conceptul de clas˘ abstract˘ cu un pas ˆ ¸ a a ınainte prin eliminareaoric˘ror implement˘ri de metode, punˆnd ˆ practic˘ unul din conceptele a a a ın aprogram˘rii orientate obiect ¸i anume cel de separare a modelului unui obiect a s(interfat˘) de implementarea sa. A¸adar, o interfat˘ poate fi privita ca un ¸a s ¸aprotocol de comunicare ˆıntre obiecte. O interfat˘ Java define¸te un set de metode dar nu specific˘ nici o imple- ¸a s amentare pentru ele. O clas˘ care implementeaz˘ o interfat˘ trebuie obligato- a a ¸ariu s˘ specifice implement˘ri pentru toate metodele interfetei, supunˆndu-se a a ¸ aa¸adar unui anumit comportament. s Definitie ¸ O interfat˘ este o colectie de metode f˘r˘ implementare ¸i declaratii de ¸a ¸ aa s ¸constante. Interfetele permit, al˘turi de clase, definirea unor noi tipuri de date. ¸ a 121
  • 123. 122 CAPITOLUL 5. INTERFETE ¸5.2 Folosirea interfetelor ¸5.2.1 Definirea unei interfete ¸Definirea unei interfete se face prin intermediul cuvˆntului cheie interface: ¸ a [public] interface NumeInterfata [extends SuperInterfata1, SuperInterfata2...] { /* Corpul interfetei: Declaratii de constane Declaratii de metode abstracte */ } O interfat˘ poate avea un singur modificator ¸i anume public. O interfat˘ ¸a s ¸apublic˘ este accesibil˘ tuturor claselor, indiferent de pachetul din care fac a aparte, implicit nivelul de acces fiind doar la nivelul pachetului din care faceparte interfata. ¸ O interfat˘ poate extinde oricˆte interfete. Acestea se numesc superinterfete ¸a a ¸ ¸¸i sunt separate prin virgul˘. (vezi ”Mo¸tenirea multipl˘ prin intermediuls a s ainterfetelor”). ¸ Corpul unei interfete poate contine: ¸ ¸ • constante: acestea pot fi sau nu declarate cu modificatorii public, static ¸i final care sunt impliciti, nici un alt modificator neputˆnd s ¸ a ap˘rea ˆ declaratia unei variabile dintr-o interfat˘. Constantele unei a ın ¸ ¸a interfete trebuie obligatoriu initializate. ¸ ¸ interface Exemplu { int MAX = 100; // Echivalent cu: public static final int MAX = 100; int MAX; // Incorect, lipseste initializarea private int x = 1; // Incorect, modificator nepermis }
  • 124. 5.2. FOLOSIREA INTERFETELOR ¸ 123 • metode f˘r˘ implementare: acestea pot fi sau nu declarate cu mod- a a ificatorul public, care este implicit; nici un alt modificator nu poate ap˘rea ˆ declaratia unei metode a unei interfete. a ın ¸ ¸ interface Exemplu { void metoda(); // Echivalent cu: public void metoda(); protected void metoda2(); // Incorect, modificator nepermis Atentie ¸ • Variabilele unei interfete sunt implicit publice chiar dac˘ nu sunt declarate ¸ a cu modificatorul public. • Variabilele unei interfete sunt implicit constante chiar dac˘ nu sunt ¸ a declarate cu modificatorii static ¸i final. s • Metodele unei interfete sunt implicit publice chiar dac˘ nu sunt declarate ¸ a cu modificatorul public. • In variantele mai vechi de Java era permis ¸i modificatorul abstract s ˆ declaratia interfetei ¸i ˆ declaratiile metodelor, ˆ a acest lucru nu ın ¸ s ın ¸ ıns˘ mai este valabil, deoarece atˆt interfata cˆt ¸i metodele sale nu pot fi a ¸ a s altfel decˆt abstracte. a5.2.2 Implementarea unei interfete ¸Implementarea uneia sau mai multor interfete de c˘tre o clas˘ se face prin ¸ a aintermediul cuvˆntului cheie implements: a class NumeClasa implements NumeInterfata sau class NumeClasa implements Interfata1, Interfata2, ...
  • 125. 124 CAPITOLUL 5. INTERFETE ¸ O clas˘ poate implementa oricˆte interfete sau poate s˘ nu implementeze a a ¸ anici una. In cazul ˆ care o clas˘ implementeaz˘ o anumit˘ interfat˘, atunci tre- ın a a a ¸abuie obligatoriu s˘ specifice cod pentru toate metodele interfetei. Din acest a ¸motiv, odat˘ creata ¸i folosit˘ la implementarea unor clase, o interfat˘ nu a s a ¸amai trebuie modificat˘, ˆ sensul c˘ ad˘ugarea unor metode noi sau schim- a ın a abarea signaturii metodelor existente vor duce la erori ˆ compilarea claselor ıncare o implementeaz˘. Evident, o clas˘ poate avea ¸i alte metode ¸i variabile a a s smembre ˆ afar˘ de cele definite ˆ interfat˘. ın a ın ¸a Atentie ¸ Modificarea unei interfete implic˘ modificarea tuturor claselor care im- ¸ aplementeaz˘ acea interfat˘. a ¸a O interfat˘ nu este o clas˘, dar orice referint˘ de tip interfat˘ poate primi ¸a a ¸a ¸aca valoare o referinta la un obiect al unei clase ce implementeaz˘ interfata ¸ a ¸respectiv˘. Din acest motiv, interfetele pot fi privite ca tipuri de date ¸i vom a ¸ sspune adesea c˘ un obiect are tipul X, unde X este o interfat˘, dac˘ acesta a ¸a aeste o instant˘ a unei clase ce implementeaz˘ interfata X. ¸a a ¸ Implementarea unei interfete poate s˘ fie ¸i o clas˘ abstract˘. ¸ a s a a5.2.3 Exemplu: implementarea unei stiveS˘ consider˘m urm˘torul exemplu. Dorim s˘ implement˘m un nou tip de a a a a adate numit Stack, care s˘ modeleze notiunea de stiv˘ de obiecte. Obiectele a ¸ ade tip stiv˘, indiferent de implementarea lor, vor trebui s˘ contin˘ metodele: a a ¸ a • push - adaug˘ un nou element in stıv˘ a a • pop - elimin˘ elementul din vˆrful stivei a a • peek - returneaz˘ varful stivei a • empty - testeaz˘ dac˘ stiva este vid˘ a a a • toString - returneaz˘ continutul stivei sub forma unui ¸ir de caractere. a ¸ s
  • 126. 5.2. FOLOSIREA INTERFETELOR ¸ 125 Din punctul de vedere al structurii interne, o stiv˘ poate fi implementat˘ a afolosind un vector sau o list˘ ˆ antuit˘, ambele solutii avˆnd avantaje ¸i a ınl˘ ¸ a ¸ a sdezavantaje. Prima solutie este mai simplu de ˆ ¸eles, ˆ timp ce a doua este ¸ ınt ınmai eficient˘ din punctul de vedere al folosirii memoriei. Deoarece nu dorim as˘ leg˘m tipul de date Stack de o anumit˘ implementare structural˘, ˆ vom a a a a ıldefini prin intermediul unei interfete. Vom vedea imediat avantajele acestei ¸abord˘ri. a Listing 5.1: Interfata ce descrie stiva ¸public interface Stack { void push ( Object item ) throws StackException ; void pop () throws StackException ; Object peek () throws StackException ; boolean empty () ; String toString () ;} Pentru a trata situatiile anormale care pot ap˘rea atunci cˆnd ˆ ¸ a a ıncerc˘mas˘ punem un element ˆ stiv˘ ¸i nu este posibil din lips˘ de memorie, sau a ın a s aˆıncerc˘m s˘ acces˘m vˆrful stivei ¸i aceasta este vid˘, vom defini o exceptie a a a a s a ¸proprie StackException: Listing 5.2: Tipul de exceptie generat de stiv˘ ¸ apublic class StackException extends Exception { public StackException () { super () ; } public StackException ( String msg ) { super ( msg ) ; }} D˘m ˆ continuare prima implementare a stivei, folosind un vector: a ın Listing 5.3: Implementarea stivei folosind un vector// Implementarea stivei folosind un vector de obiecte .public class StackImpl1 implements Stack { private Object items []; // Vectorul ce contine obiectele
  • 127. 126 CAPITOLUL 5. INTERFETE ¸ private int n =0; // Numarul curent de elemente din stiva public StackImpl1 ( int max ) { // Constructor items = new Object [ max ]; } public StackImpl1 () { this (100) ; } public void push ( Object item ) throws StackException { if ( n == items . length ) throw new StackException ( " Stiva este plina ! " ) ; items [ n ++] = item ; } public void pop () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; items [ - - n ] = null ; } public Object peek () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; return items [n -1]; } public boolean empty () { return ( n ==0) ; } public String toString () { String s = " " ; for ( int i =n -1; i >=0; i - -) s += items [ i ]. toString () + " " ; return s ; }} Remarcati c˘, de¸i ˆ interfat˘ metodele nu sunt declarate explicit cu ¸ a s ın ¸amodificatorul public, ele sunt totu¸i publice ¸i trebuie declarate ca atare ˆ s s ınclas˘. aTrebuie remarcat ¸i faptul c˘ metoda toString este definit˘ deja ˆ clasa s a a ınObject, deci clasa noastr˘ o are deja implementat˘ ¸i nu am fi obtinut nici a as ¸o eroare la compilare dac˘ nu o implementam explicit. Ceea ce facem acum aeste de fapt supradefinirea ei.
  • 128. 5.2. FOLOSIREA INTERFETELOR ¸ 127O alt˘ observatie important˘ se refer˘ la faptul c˘ trebuie s˘ declar˘m ˆ a ¸ a a a a a ıncadrul interfetei ¸i exceptiile aruncate de metode, ce trebuie obligatoriu ¸ s ¸tratate. S˘ vedem acum modalitatea de implementare a stivei folosind o list˘ a aˆ antuit˘:ınl˘ ¸ a Listing 5.4: Implementarea stivei folosind o list˘ a// Implementarea stivei folosind o lista inlantuita .public class StackImpl2 implements Stack { class Node { // Clasa interna ce reprezinta un nod al listei Object item ; // informatia din nod Node link ; // legatura la urmatorul nod Node ( Object item , Node link ) { this . item = item ; this . link = link ; } } private Node top = null ; // Referinta la varful stivei public void push ( Object item ) { Node node = new Node ( item , top ) ; top = node ; } public void pop () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; top = top . link ; } public Object peek () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; return top . item ; } public boolean empty () { return ( top == null ) ; } public String toString () { String s = " " ; Node node = top ; while ( node != null ) {
  • 129. 128 CAPITOLUL 5. INTERFETE ¸ s += ( node . item ) . toString () + " " ; node = node . link ; } return s ; }} Singura observatie pe care o facem aici este c˘, de¸i metoda push din ¸ a sinterfat˘ declar˘ aruncarea unor exceptii de tipul StackException, nu este ¸a a ¸obligatoriu ca metoda din clas˘ s˘ specifice ¸i ea acest lucru, atˆt timp cˆt a a s a anu genereaz˘ exceptii de acel tip. Invers este ˆ a obligatoriu. a ¸ ıns˘ In continuare este prezentat˘ o mic˘ aplicatie demonstrativ˘ care folose¸te a a ¸ a stipul de date nou creat ¸i cele dou˘ implement˘ri ale sale: s a a Listing 5.5: Folosirea stiveipublic class TestStiva { public static void afiseaza ( Stack s ) { System . out . println ( " Continutul stivei este : " + s ) ; } public static void main ( String args []) { try { Stack s1 = new StackImpl1 () ; s1 . push ( " a " ) ; s1 . push ( " b " ) ; afiseaza ( s1 ) ; Stack s2 = new StackImpl2 () ; s2 . push ( new Integer (1) ) ; s2 . push ( new Double (3.14) ) ; afiseaza ( s2 ) ; } catch ( StackException e ) { System . err . println ( " Eroare la lucrul cu stiva ! " ) ; e . printStackTrace () ; } }} Observati folosirea interfetei Stack ca un tip de date, ce aduce flexibilitate ¸ ¸sporit˘ ˆ manevrarea claselor ce implementeaz˘ tipul respectiv. Metoda a ın a
  • 130. 5.3. INTERFETE SI CLASE ABSTRACTE ¸ ¸ 129afiseaza accept˘ ca argument orice obiect al unei clase ce implementeaz˘ a aStack. Observatie¸ In pachetul java.util exist˘ clasa Stack care modeleaz˘ notiune de stiv˘ a a ¸ ade obiecte ¸i, evident, aceasta va fi folosit˘ ˆ aplicatiile ce au nevoie de acest s a ın ¸tip de date. Exemplu oferit de noi nu are leg˘tur˘ cu aceast˘ clas˘ ¸i are rol a a a aspur demonstrativ.5.3 Interfete ¸i clase abstracte ¸ sLa prima vedere o interfat˘ nu este altceva decˆt o clas˘ abstract˘ ˆ care ¸a a a a ıntoate metodele sunt abstracte (nu au nici o implementare).A¸adar, o clas˘ abstract˘ nu ar putea ˆ s a a ınlocui o interfat˘ ? ¸aRaspunsul la intrebare depinde de situatie, ˆ a ˆ general este ’Nu’. ¸ ıns˘ ınDeosebirea const˘ ˆ faptul c˘ unele clase sunt fortate s˘ extind˘ o anumit˘ a ın a ¸ a a aclas˘ (de exemplu orice applet trebuie s˘ fie subclasa a clasei Applet) ¸i nu ar a a smai putea sa extind˘ o alt˘ clas˘, deoarece ˆ Java nu exista decˆt mo¸tenire a a a ın a ssimpla. Fara folosirea interfetelor nu am putea forta clasa respectiv˘ s˘ ¸ ¸ a arespecte diverse tipuri de protocoale. La nivel conceptual, diferenta const˘ ˆ ¸ a ın: • extinderea unei clase abstracte forteaz˘ o relatie ˆ ¸ a ¸ ıntre clase; • implementarea unei interfete specific˘ doar necesitatea implement˘rii ¸ a a unor anumie metode. In multe situatii interfetele ¸i clasele abstracte sunt folosite ˆ ¸ ¸ s ımpreun˘apentru a implementa cˆt mai flexibil ¸i eficient o anumit˘ ierarhie de clase. Un a s aexemplu sugestiv este dat de clasele ce descriu colectii. Ca sa particulariz˘m, ¸ aexist˘: a • interfata List care impune protocolul pe care trebuie s˘ ˆ respecte o ¸ a ıl clas˘ de tip list˘, a a • clasa abstract˘ AbstractList care implementeaz˘ interfata List ¸i a a ¸ s ofer˘ implement˘ri concrete pentru metodele comune tuturor tipurilor a a de list˘, a
  • 131. 130 CAPITOLUL 5. INTERFETE ¸ • clase concrete, cum ar fi LinkedList, ArrayList care extind AbstractList.5.4 Mo¸tenire multipl˘ prin interfete s a ¸Interfetele nu au nici o implementare ¸i nu pot fi instantiate. Din acest motiv, ¸ s ¸nu reprezint˘ nici o problem˘ ca anumite clase s˘ implementeze mai multe a a ainterfete sau ca o interfat˘ s˘ extind˘ mai multe interfete (s˘ aib˘ mai multe ¸ ¸a a a ¸ a asuperinterfete) ¸ class NumeClasa implements Interfata1, Interfata2, ... interface NumeInterfata extends Interfata1, Interfata2, ...O interfat˘ mosteneste atˆt constantele cˆt ¸i declaratiile de metode de la ¸a a a s ¸superinterfetele sale. O clas˘ mo¸teneste doar constantele unei interfete ¸i ¸ a s ¸ sresponsabilitatea implement˘rii metodelor sale. a S˘ consider˘m un exemplu de clasa care implementeaza mai multe interfete: a a ¸ interface Inotator { void inoata(); } interface Zburator { void zboara(); } interface Luptator { void lupta(); } class Erou implements Inotator, Zburator, Luptator { public void inoata() {} public void zboara() {} public void lupta() {} } Exemplu de interfat˘ care extinde mai multe interfete: ¸a ¸ interface Monstru { void ameninta(); } interface MonstruPericulos extends Monstru { void distruge();
  • 132. ¸ ˘5.4. MOSTENIRE MULTIPLA PRIN INTERFETE ¸ 131 } interface Mortal { void omoara(); } interface Vampir extends MonstruPericulos, Mortal { void beaSange(); } class Dracula implements Vampir { public void ameninta() {} public void distruge() {} public void omoara()() {} public void beaSange() {} } Evident, pot ap˘rea situatii de ambiguitate, atunci cˆnd exist˘ constante a ¸ a asau metode cu acelea¸i nume ˆ mai multe interfete, ˆ a acest lucru trebuie s ın ¸ ıns˘ˆıntotdeauna evitat, deoarece scrierea unui cod care poate fi confuz este un stilprost de programare. In cazul in care acest lucru se ˆ ampl˘, compilatorul ıntˆ anu va furniza eroare decˆt dac˘ se ˆ a a ıncearc˘ referirea constantelor ambigue af˘r˘ a le prefixa cu numele interfetei sau dac˘ metodele cu acela¸i nume nu aa ¸ a spot fi deosbite, cum ar fi situatia cˆnd au aceea¸i list˘ de argumente dar ¸ a s atipuri returnate incompatibile.interface I1 { int x=1; void metoda();}interface I2 { int x=2; void metoda(); //corect //int metoda(); //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
  • 133. 132 CAPITOLUL 5. INTERFETE ¸ }} S˘ recapitul˘m cˆteva lucruri legate de clase ¸i interfete: a a a s ¸ • O clas˘ nu poate avea decˆt o superclas˘. a a a • O clas˘ poate implementa oricˆte interfete. a a ¸ • O clas˘ trebuie obligatoriu s˘ trateze metodele din interfetele pe care a a ¸ la implementeaz˘. a • Ierarhia interfetelor este independent˘ de ierarhia claselor care le im- ¸ a plementeaz˘.a5.5 Utilitatea interfetelor ¸Dup˘ cum am v˘zut, o interfat˘ define¸te un protocol ce poate fi implementat a a ¸a sde orice clas˘, indiferent de ierarhia de clase din care face parte. Interfetele a ¸sunt utile pentru: • definirea unor similaritati ˆ ıntre clase independente f˘r˘ a forta artificial aa ¸ o legatur˘ ˆ a ıntre ele; • asigur˘ c˘ toate clasele care implementeaz˘ o interfat˘ pun la dipozitie a a a ¸a ¸ metodele specificate ˆ interfat˘ - de aici rezult˘ posibilitatea imple- ın ¸a a ment˘rii unor clase prin mai multe modalit˘¸i ¸i folosirea lor ˆ a at s ıntr-o manier˘ unitar˘; a a • definirea unor grupuri de constante; • transmiterea metodelor ca parametri;5.5.1 Crearea grupurilor de constanteDeoarece orice variabil˘ a unei interfete este implicit declarat˘ cu public, a ¸ astatic si final, interfetele reprezint˘ o metod˘ convenabil˘ de creare a unor ¸ a a agrupuri de constante care s˘ fie folosite global ˆ a ıntr-o aplicatie: ¸
  • 134. 5.5. UTILITATEA INTERFETELOR ¸ 133 public interface Luni { int IAN=1, FEB=2, ..., DEC=12; }Folosirea acestor constante se face prin expresii de genulNumeInterfata.constanta, ca ˆ exemplul de mai jos: ın if (luna < Luni.DEC) luna ++ else luna = Luni.IAN;5.5.2 Transmiterea metodelor ca parametriDeoarece nu exist˘ pointeri propriu-zi¸i, transmiterea metodelor ca parametri a seste realizat˘ ˆ Java prin intermediul interfetelor. Atunci cˆnd o metod˘ a ın ¸ a atrebuie s˘ primeasc˘ ca argument de intrare o referint˘ la o alt˘ functie a a ¸a a ¸necesar˘ executiei sale, cunoscut˘ doar la momentul executiei, atunci argu- a ¸ a ¸mentul respectiv va fi declarat de tipul unei interfete care contine metoda ¸ ¸respectiv˘. La executie metoda va putea primi ca parametru orice obiect ce a ¸implementeaz˘ interfata respectiv˘ ¸i deci contine ¸i codul functiei respective, a ¸ as ¸ s ¸aceasta urmˆnd s˘ fie apelat˘ normal pentru a obtine rezultatul dorit. a a a ¸ Aceast˘ tehnic˘, denumit˘ ¸i call-back, este extrem de folosit˘ ˆ Java a a a s a ın¸i trebuie neap˘rat ˆ ¸eleas˘. S˘ consider˘m mai multe exemple pentru as a ınt a a aclarifica lucrurile. Primul exemplu se refer˘ la explorarea nodurilor unui graf. In fiecare anod trebuie s˘ se execute prelucrarea informatiei din nodul respectiv prin a ¸intermediul unei functii primite ca parametru. Pentru aceasta, vom defini o ¸interfata Functie care va specifica metoda trimis˘ ca parametru. ¸˘ ainterface Functie { public void executa(Nod u);}class Graf { //... void explorare(Functie f) { //...
  • 135. 134 CAPITOLUL 5. INTERFETE ¸ if (explorarea a ajuns in nodul v) { f.executa(v); //... } }}//Definim doua functiiclass 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 fi prezentat ˆ sectiunea urm˘toare, ˆ ın ¸ a ıntrucˆt face aparte din API-ul standard Java ¸i vor fi puse ˆ evident˘, prin intermediul s ın ¸as˘u, ¸i alte tehnici de programare. a s5.6 Interfata FilenameFilter ¸Instantele claselor ce implementeaz˘ aceasta interfat˘ sunt folosite pentru a ¸ a ¸acrea filtre pentru fi¸iere ¸i sunt primite ca argumente de metode care listeaz˘ s s acontinutul unui director, cum ar fi metoda list a clasei File. ¸A¸adar, putem spune c˘ metoda list prime¸te ca argument o alt˘ functie s a s a ¸care specific˘ dac˘ un fi¸ier va fi returnat sau nu (criteriul de filtrare). a a s
  • 136. 5.6. INTERFATA FILENAMEFILTER ¸ 135 Interfata FilenameFilter are o singur˘ metod˘: accept care specific˘ ¸ a a acriteriul de filtrare ¸i anume, testeaz˘ dac˘ numele fi¸ierului primit ca para- s a a smetru ˆ ındepline¸te conditiile dorite de noi. s ¸ Definitia interfetei este: ¸ ¸ public interface FilenameFilter { public boolean accept(File dir, String numeFisier); } A¸adar, orice clas˘ de specificare a unui filtru care implementez˘ interfata s a a ¸FilenameFilter trebuie s˘ implementeze metoda accept a acestei interfete. a ¸Aceste clase mai pot avea ¸i alte metode, de exemplu un constructor care s˘ s aprimeasca criteriul de filtrare. In general, o clas˘ de specificare a unui filtru aare urm˘torul format: aclass 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 filtrarea fi¸ierelor ssunt: • endsWith - testeaz˘ dac˘ un ¸ir are o anumit˘ terminatie a a s a ¸ • indexOf - testeaz˘ dac˘ un ¸ir contine un anumit sub¸ir, returnˆnd a a s ¸ s a pozitia acestuia, sau 0 ˆ caz contrar. ¸ ın
  • 137. 136 CAPITOLUL 5. INTERFETE ¸ Instantele claselor pentru filtrare sunt primite ca argumente de metode ¸de listare a continutului unui director. O astfel de metod˘ este list din ¸ aclasa File: String[] list (FilenameFilter filtru) Observati c˘ aici interfata este folosit˘ ca un tip de date, ea fiind substi- a ¸ atuit˘ cu orice clas˘ care o implementeaz˘. Acesta este un exemplu tipic de a a atransmitere a unei functii (functia de filtrare accept) ca argument al unei ¸ ¸metode. S˘ consider˘m exemplul complet ˆ care dorim s˘ list˘m fi¸ierele din di- a a ın a a srectorul curent care au o anumit˘ extensie. a Listing 5.6: Listarea fi¸ierelor cu o anumit˘ extensie s a/* Listarea fisierelor din directorul curent care au anumita extensie primita ca argument . Daca nu se primeste nici un argument , vor fi listate toate .*/import java . io .*;class Listare { public static void main ( String [] args ) { try { File director = new File ( " . " ) ; String [] list ; if ( args . length > 0) list = director . list ( new Filtru ( args [0]) ) ; else list = director . list () ; for ( int i = 0; i < list . length ; i ++) System . out . println ( list [ i ]) ; } catch ( Exception e ) { e . printStackTrace () ; } }}class Filtru implements FilenameFilter { String extensie ; Filtru ( String extensie ) { this . extensie = extensie ;
  • 138. 5.6. INTERFATA FILENAMEFILTER ¸ 137 } public boolean accept ( File dir , String nume ) { return ( nume . endsWith ( " . " + extensie ) ) ; }}5.6.1 Folosirea claselor anonimeIn cazul ˆ care nu avem nevoie de clasa care reprezint˘ filtrul pentru listarea ın afi¸ierelor dintr-un director decˆt o singur˘ dat˘, pentru a evita crearea unei s a a anoi clase de sine st˘t˘toare care s˘ fie folosit˘ pentru instantierea unui singur aa a a ¸obiect, putem folosi clas˘ intern˘ anonim˘, aceast˘ situatie fiind un exemplu a a a a ¸tipic de folosire a acestora. Listing 5.7: Folosirea unei clase anonime/* Listarea fisierelor din directorul curent folosind o clasa anonima pentru filtru .*/import java . io .*;class Listare { public static void main ( String [] args ) { try { File director = new File ( " . " ) ; String [] list ; if ( args . length > 0) { final String extensie = args [0]; list = director . list ( new FilenameFilter () { // Clasa interna anonima public boolean accept ( File dir , String nume ) { return ( nume . endsWith ( " . " + extensie ) ) ; } }) ; } else list = director . list () ; for ( int i = 0; i < list . length ; i ++) System . out . println ( list [ i ]) ; } catch ( Exception e ) { e . printStackTrace () ; }
  • 139. 138 CAPITOLUL 5. INTERFETE ¸ }} A¸adar, o modalitate uzual˘ de folosire a claselor anonime pentru instantierea s a ¸unui obiect care trebuie s˘ respecte o interfat˘ este: a ¸a metoda(new Interfata() { // Implementarea metodelor interfetei });5.7 Compararea obiectelorAm v˘zut ˆ primul capitol c˘ o solutie facil˘ ¸i eficient˘ de sortare a unui a ın a ¸ as avector este folosirea metodei sort din clasa java.util.Arrays. int v[]={3, 1, 4, 2}; java.util.Arrays.sort(v); // Sorteaza vectorul v // Acesta va deveni {1, 2, 3, 4} In cazul ˆ care elementele din vector sunt de tip primitiv, ca in exem- ınplul de mai sus, nu exist˘ nici o problem˘ ˆ a determina ordinea fireasc˘ a a ın aa elementelor. Ce se ˆ ampl˘ ˆ a atunci cˆnd vectorul contine referinte la ınt˘ a ıns˘ a ¸ ¸obiecte de un anumit tip ? S˘ consider˘m urm˘torul exemplu, ˆ care dorim a a a ıns˘ sort˘m un vector format din instante ale clasei Persoana, definit˘ mai jos: a a ¸ a Listing 5.8: Clasa Persoana (f˘r˘ suport pentru comparare) aaclass Persoana { int cod ; String nume ; public Persoana ( int cod , String nume ) { this . cod = cod ; this . nume = nume ; } public String toString () { return cod + " t " + nume ; }}
  • 140. 5.7. COMPARAREA OBIECTELOR 139 Programul urm˘tor ar trebui s˘ sorteze un vector de persoane: a a Listing 5.9: Sortarea unui vector de tip referint˘ ¸aclass 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 ]) ; }} La executia acestei aplicatii va fi obtinut˘ o exceptie, deoarece metoda ¸ ¸ ¸ a ¸sort nu ¸tie care este ordinea natural˘ a obiectelor de tip Persoana. Va s atrebui, ˆ ıntr-un fel sau altul, s˘ specific˘m acest lucru. a a5.7.1 Interfata Comparable ¸Interfata Comparable impune o ordine total˘ asupra obiectelor unei clase ce ¸ ao implementeaz˘. Aceast˘ ordine se nume¸te ordinea natural˘ a clasei ¸i este a a s a sspecificat˘ prin intermediul metodei compareTo. Definitia interfetei este: a ¸ ¸ public interface Comparable { int compareTo(Object o); } A¸adar, o clas˘ ale c˘rei instante trebuie s˘ fie comparabil va implementa s a a ¸ ametoda compareTo care trebuie s˘ returneze: a • o valoare strict negativ˘: dac˘ obiectul curent (this) este mai mic a a decˆ obiectul primit ca argument; a • zero: dac˘ obiectul curent este egal decˆ obiectul primit ca argument; a a
  • 141. 140 CAPITOLUL 5. INTERFETE ¸ • o valoare strict pozitiv˘: dac˘ obiectul curent este mai mare decˆ a a a obiectul primit ca argument. Reamintim c˘ metoda equals, mo¸tenit˘ din Object de toate clasele, a s adetermin˘ dac˘ dou˘ obiecte sunt egale (au aceea¸i valoare). Spunem c˘ a a a s aordinea natural˘ a unei clase C este consitent˘ cu equals dac˘ ¸i numai a a a sdac˘ (e1.compareTo((Object)e2) == 0) are aceeas¸i valoare logic˘ cu a s ae1.equals((Object)e2, pentru orice e1, e2 instante ale lui C. ¸ null nu este instant˘ a nici unei clase ¸i e.compareTo(null) trebuie s˘ ¸a s aarunce o exceptie de tip NullPointerException chiar dac˘ e.equals(null) ¸ areturneaz˘ false. a S˘ presupunem c˘ dorim ca ordinea natural˘ a persoanelor s˘ fie dup˘ a a a a acodul lor intern. Listing 5.10: Clasa Persoana cu suport pentru comparareclass Persoana implements Comparable { int cod ; String nume ; public Persoana ( int cod , String nume ) { this . cod = cod ; this . nume = nume ; } public String toString () { return cod + " t " + nume ; } public boolean equals ( Object o ) { if (!( o instanceof Persoana ) ) return false ; Persoana p = ( Persoana ) o ; return ( cod == p . cod ) && ( nume . equals ( p . nume ) ) ; } public int compareTo ( Object o ) { if ( o == null ) throw new Null P o i n t e r E x c e p t i o n () ; if (!( o instanceof Persoana ) ) throw new Clas sCas tExce ptio n ( " Nu pot compara ! " ) ; Persoana p = ( Persoana ) o ;
  • 142. 5.7. COMPARAREA OBIECTELOR 141 return ( cod - p . cod ) ; }} Observati folosirea operatorului instanceof, care verific˘ dac˘ un obiect ¸ a aeste instant˘ a unei anumite clase. Metoda compareTo va arunca o exceptie ¸a ¸de tipul ClassCastException dac˘ se ˆ a ıncearc˘ compararea unui obiect de atip Persoana cu un obiect de alt tip. Metoda equals va returna, pur ¸i ssimplu, false.5.7.2 Interfata Comparator ¸In cazul ˆ care dorim s˘ sort˘m elementele unui vector ce contine referinte ın a a ¸ ¸dup˘ alt criteriu decˆt ordinea natural˘ a elemenetelor, avem nevoie de o alt˘ a a a asolutie. Aceasta este oferit˘ tot de metoda sort din clasa java.util.Arrays, ¸ adar ˆ varianta ˆ care, pe lˆng˘ vectorul ce trebuie sortat, vom transmite ın ın a aun argument de tip Comparator care s˘ specifice modalitatea de comparare aa elementelor. Interfata java.util.Comparator contine metoda compare, care impune ¸ ¸o ordine total˘ asupra elementelor unei colectii. Aceasta returneaz˘ un ˆ a ¸ a ıntregcu aceea¸i semnificatie ca la metoda compareTo a interfetei Comparator ¸i s ¸ ¸ sare urm˘toarea definitie: int compare(Object o1, Object o2); a ¸ S˘ presupunem c˘ dorim s˘ sort˘m persoanele ordonate dup˘ numele lor. a a a a aPentru definirea comparatorului vom folosi o clas˘ anonim˘. a a Listing 5.11: Sortarea unui vector folosind un comparatorimport 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 ) ) ;
  • 143. 142 CAPITOLUL 5. INTERFETE ¸ } }) ; System . out . println ( " Persoanele ordonate dupa nume : " ) ; for ( int i =0; i < p . length ; i ++) System . out . println ( p [ i ]) ; }} Observati cum compararea a dou˘ ¸iruri de caractere se face tot cu metoda ¸ ascompareTo, clasa String implemenˆnd interfata Comparable. a ¸5.8 AdaptoriIn cazul ˆ care o interfat˘ contine mai multe metode ¸i, la un moment ın ¸a ¸ sdat, avem nevoie de un obiect care implementeaz˘ interfata respectiv dar nu a ¸specific˘ cod decˆt pentru o singur˘ metod˘, el trebui totu¸i s˘ implementeze a a a a s atoate metodele interfetei, chiar dac˘ nu specific˘ nici un cod. ¸ a a interface X { void metoda_1(); void metoda_2(); ... void metoda_n(); } ... // Avem nevoie de un obiect de tip X // ca argument al unei functii functie(new X() { public void metoda_1() { // Singura metoda care ne intereseaza ... } // Trebuie sa apara si celelalte metode // chiar daca nu au implementare efectiva public void metoda_2() {} public void metoda_3() {} ... public void metoda_n() {}
  • 144. 5.8. ADAPTORI 143 }); Aceast˘ abordare poate fi nepl˘cut˘ dac˘ avem frecvent nevoie de obiecte a a a aale unor clase ce implementeaz˘ interfata X. Solutia la aceast˘ problem˘ este a ¸ ¸ a afolosirea adaptorilor. Definitie ¸ Un adaptor este o clas˘ abstract˘ care implementeaz˘ o anumit˘ interfat˘ a a a a ¸af˘r˘ a specifica cod nici unei metode a interfetei. aa ¸ public abstract class XAdapter implements X { public void metoda_1() {} public void metoda_2() {} ... public void metoda_n() {} } In situatia cˆnd avem nevoie de un obiect de tip X vom folosi clasa ¸ aabstract˘, supradefinind doar metoda care ne intereseaz˘: a a functie(new XAdapter() { public void metoda_1() { // Singura metoda care ne intereseaza ... } }); Mai multe exemple de folosire a adaptorilor vor fi date ˆ capitolul ”Interfata ın ¸grafic˘ cu utilizatorul”. a
  • 145. 144 CAPITOLUL 5. INTERFETE ¸
  • 146. Capitolul 6Organizarea claselor6.1 PacheteDefinitie ¸ Un pachet este o colectie de clase ¸i interfete ˆ ¸ s ¸ ınrudite din punctul devedere al functionalit˘¸ii lor. Sunt folosite pentru g˘sirea ¸i utilizarea mai ¸ at a su¸oar˘ a claselor, pentru a evita conflictele de nume ¸i pentru a controla s a saccesul la anumite clase. In alte limbaje de programare pachetele se mainumesc libr˘rii sau bibilioteci. a6.1.1 Pachetele standard (J2SDK)Platforma standard de lucru Java se bazeaz˘ pe o serie de pachete cu ajutorul ac˘rora se pot construi ˆ a ıntr-o manier˘ simplificat˘ aplicatiile. Exist˘ deci un a a ¸ aset de clase deja implementate care modeleaz˘ structuri de date, algoritmi sau adiverse notiuni esentiale ˆ dezvoltarea unui program. Cele mai importante ¸ ¸ ınpachete ¸i suportul oferit de lor sunt: s • java.lang - clasele de baz˘ ale limbajului Java a • java.io - intr˘ri/ie¸iri, lucrul cu fi¸iere a s s • java.util - clase ¸i interfete utile s ¸ • java.applet - dezvoltarea de appleturi 145
  • 147. 146 CAPITOLUL 6. ORGANIZAREA CLASELOR • java.awt - interfata grafic˘ cu utilizatorul ¸ a • java.awt.event - mecanismele de tratare e evenimentelor generate de utilizator • java.beans - scrierea de componente reutilizabile • java.net - programare de retea ¸ • java.sql - lucrul cu baze de date • java.rmi - executie la distant˘ Remote Message Interface ¸ ¸a • java.security - mecanisme de securitate: criptare, autentificare • java.math - operatii matematice cu numere mari ¸ • java.text - lucrul cu texte, date ¸i numere independent de limb˘ s a • java.lang.reflect - introspectie ¸ • javax.swing - interfata grafic˘ cu utilizatorul, mult ˆ ¸ a ımbog˘¸it˘ fat˘ de at a ¸a AWT. • ...6.1.2 Folosirea membrilor unui pachetConform specificatiilor de acces ale unei clase ¸i ale mebrilor ei, doar clasele ¸ spublice ¸i membrii declarati publici ai unei clase sunt accesibili ˆ afara pa- s ¸ ınchetului ˆ care se g˘sesc. Dup˘ cum am v˘zut ˆ sectiunea ”Specificatori ın a a a ın ¸de acces pentru membrii unei clase”, accesul implicit ˆ Java este la nivel de ınpachet. Pentru a folosi o clas˘ public˘ dintr-un anumit pachet, sau pentru a apela a ao metod˘ public˘ a unei clase publice a unui pachet, exist˘ trei solutii: a a a ¸ • specificarea numelui complet al clasei • importul clasei respective • importul ˆ ıntregului pachet ˆ care se g˘se¸te clasa. ın a s
  • 148. 6.1. PACHETE 147 Specificarea numelui complet al clasei se face prin prefixarea numelui scurtal clasei cu numele pachetului din care face parte: numePachet.NumeClasa. Button - numele scurt al clasei java.awt - pachetul din care face parte java.awt.Button - numele complet al clasei Aceast˘ metod˘ este recomandat˘ doar pentru cazul ˆ care folosirea a a a ınacelei clase se face o singur˘ dat˘ sau foarte rar. a a De exemplu, ar fi extrem de nepl˘cut s˘ scriem de fiecare dat˘ cˆnd vrem a a a as˘ declar˘m un obiect grafic secvente de genul: a a ¸ java.awt.Button b1 = new java.awt.Button("OK"); java.awt.Button b2 = new java.awt.Button("Cancel"); java.awt.TextField tf1 = new java.awt.TextField("Neplacut"); java.awt.TextField tf2 = new java.awt.TextField("Tot neplacut"); In aceste situatii, vom importa ˆ aplicatia noastr˘ clasa respectiv˘, sau ¸ ın ¸ a aˆıntreg pachetul din care face parte. Acest lucru se realizeaz˘ prin instructiunea a ¸import, care trebuie s˘ apar˘ la ˆ a a ınceputul fi¸ierelor surs˘, ˆ s a ınainte de declarareavreunei clase sau interfete. ¸6.1.3 Importul unei clase sau interfete ¸Se face prin instructiunea import ˆ care specific˘m numele complet al clasei ¸ ın asau interfetei pe care dorim s˘ o folosim dintr-un anumit pacehet: ¸ a import numePachet.numeClasa; //Pentru exemplul nostru: import java.awt.Button; import java.awt.TextField; Din acest moment, vom putea folosi ˆ clasele fi¸ierului ˆ care am plasat ın s ıninstructiunea de import numele scurt al claselor Button ¸i TextField: ¸ s Button b1 = new Button("OK"); Button b2 = new Button("Cancel"); TextField tf1 = new TextField("Placut"); TextField tf2 = new TextField("Foarte placut");
  • 149. 148 CAPITOLUL 6. ORGANIZAREA CLASELOR Aceast˘ abordare este eficient˘ ¸i recomandat˘ ˆ cazul ˆ care nu avem a as a ın ınnevoie decˆt de cˆteva clase din pachetul respectiv. Dac˘ ˆ exemplul nostru a a a ınam avea nevoie ¸i de clasele Line, Point, Rectangle, Polygon, ar trebui s˘ s aavem cˆte o instructiune de import pentru fiecare dintre ele: a ¸ import java.awt.Button; import java.awt.TextField; import java.awt.Rectangle; import java.awt.Line; import java.awt.Point; import java.awt.Polygon;In aceast˘ situatie ar fi mai simplu s˘ folosim importul la cerere din ˆ a ¸ a ıntregulpachet ¸i nu al fiec˘rei clase ˆ parte. s a ın6.1.4 Importul la cerere dintr-un pachetImportul la cerere dintr-un anumit pachet se face printr-o instructiune import ¸ˆ care specific˘m numele pachetului ale c˘rui clase ¸i interfete dorim s˘ leın a a s ¸ afolosim, urmat de simbolul *. Se nume¸te import la cerere deoarece ˆ arcarea s ınc˘claselor se face dinamic, ˆ momentul apel˘rii lor. ın a import numePachet.*; //Pentru exemplul nostru: import java.awt.*;Din acest moment, vom putea folosi ˆ clasele fi¸ierului ˆ care am plasat ın s ıninstructiunea de import numele scurt al tuturor claselor pachetului importat: ¸ Button b = new Button("OK"); Point p = new Point(0, 0); Atentie ¸ * nu are semnificatia uzual˘ de la fi¸iere de wildcard (masc˘) ¸i nu poate fi ¸ a s a sfolosit decˆt ca atare. O expresie de genul import java.awt.C*; va produce ao eroare de compilare.
  • 150. 6.1. PACHETE 149 In cazul ˆ care sunt importate dou˘ sau mai multe pachete care contin ın a ¸clase (interfete) cu acela¸i nume, atunci referirea la ele trebuie f˘cut˘ doar ¸ s a afolosind numele complet, ˆ caz contrar fiind semnalat˘ o ambiguitate de ın ac˘tre compilator. a import java.awt.*; // Contine clasa List import java.util.*; // Contine interfata List ... List x; //Declaratie ambigua java.awt.List a = new java.awt.List(); //corect java.util.List b = new ArrayList(); //corect Sunt considerate importate automat, pentru orice fi¸ier surs˘, urm˘toarele s a apachete: • pachetul java.lang import java.lang.*; // Poate sau nu sa apara // Mai bine nu... • pachetul curent • pachetul implicit (f˘r˘ nume) aa6.1.5 Importul staticAceast˘ facilitate, introdus˘ ˆ a a ıncepˆnd cu versiunea 1.5, permite referirea con- astantelor statice ale unei clase f˘r˘ a mai specifica numele complet al aces- aateia ¸i este implementat˘ prin ad˘ugarea cuvˆntului cheie static dup˘ cel de s a a a aimport: import static numePachet.NumeClasa.*;Astfel, ˆ loc s˘ ne referim la constantele clasei cu expresii de tipul ın aNumeClasa.CONSTANTA, putem folosi doar numele constantei.
  • 151. 150 CAPITOLUL 6. ORGANIZAREA CLASELOR // Inainte de versiuna 1.5 import java.awt.BorderLayout.*; ... fereastra.add(new Button(), BorderLayout.CENTER); // Incepand cu versiunea 1.5 import java.awt.BorderLayout.*; import static java.awt.BorderLayout.*; ... fereastra.add(new Button(), CENTER); Atentie ¸ Importul static nu import˘ decˆt constantele statice ale unei clase, nu ¸i a a sclasa ˆ sine. ın6.1.6 Crearea unui pachetToate clasele ¸i interfetele Java apartin la diverse pachete, grupate dup˘ s ¸ afunctionalitatea lor. Dup˘ cum am v˘zut clasele de baz˘ se g˘sesc ˆ pa- ¸ a a a a ınchetul java.lang, clasele pentru intr˘ri/ie¸iri sunt ˆ java.io, clasele pentru a s ıninterfata grafic˘ ˆ java.awt, etc. ¸ a ın Crearea unui pachet se realizeaz˘ prin scriere la ˆ a ınceputul fi¸ierelor surs˘ s ace contin clasele ¸i interfetele pe care dorim s˘ le grup˘m ˆ ¸ s ¸ a a ıntr-un pachet ainstructiunii: package numePachet; ¸ S˘ consider˘m un exemplu: presupunem c˘ avem dou˘ fi¸iere surs˘ Graf.java a a a a s a¸i Arbore.java.s//Fisierul Graf.java package grafuri; class Graf {...} class GrafPerfect extends Graf {...}//Fisierul Arbore.java package grafuri; class Arbore {...}
  • 152. 6.1. PACHETE 151 class ArboreBinar extends Arbore {...} Clasele Graf, GrafPerfect, Arbore, ArboreBinar vor face parte dinacela¸i pachet grafuri. s Instructiunea package actioneaz˘ asupra ˆ ¸ ¸ a ıntregului fi¸ier surs˘ la ˆ s a ınceputulc˘ruia apare. Cu alte cuvinte nu putem specifica faptul c˘ anumite clase a adintr-un fi¸ier surs˘ apartin unui pachet, iar altele altui pachet. s a ¸ Dac˘ nu este specificat un anumit pachet, clasele unui fi¸ier surs˘ vor face a s aparte din pachetul implicit (care nu are nici un nume). In general, pachetulimplicit este format din toate clasele ¸i intefetele directorului curent de lucru. s ¸Este recomandat ˆ a ca toate clasele ¸i intefetele s˘ fie plasate ˆ pachete, ıns˘ s a ınpachetul implicit fiind folosit doar pentru aplicatii mici sau prototipuri. ¸6.1.7 Denumirea unui pachetExist˘ posibilitatea ca doi programatori care lucreaz˘ la un proiect comun a as˘ foloseasc˘ acela¸i nume pentru unele din clasele lor. De asemenea, se a a spoate ca una din clasele unei aplicatii s˘ aib˘ acela¸i nume cu o clas˘ a ¸ a a s amediului Java. Acest lucru este posibil atˆt timp cˆt clasele cu acela¸i nume a a sse gasesc ˆ pachete diferite, ele fiind diferentiate prin prefixarea lor cu numele ın ¸pachetelor. Ce se ˆ ampl˘ ˆ a cˆnd doi programatori care lucreaz˘ la un proiect ıntˆ a ıns˘ a acomun folosesc clase cu acela¸i nume, ce se gasesc ˆ pachete cu acela¸i nume s ın s? Pentru a evita acest lucru, companiile folosesc inversul domeniului lorInternet ˆ denumirea pachetelor implementate ˆ cadrul companiei, cum ın ınar fi ro.companie.numePachet. In cadrul aceleiasi companii, conflictele denume vor fi rezolvate prin diverse conventii de uz intern, cum ar fi folosirea ¸numelui de cont al programatorilor ˆ denumirea pachetelor create de ace¸tia. ın sDe exemplu, programatorul cu numele Ion al companiei XSoft, avˆnd contul aion@xsoft.ro, ˆsi va prefixa pachetele cu ro.xsoft.ion, pentru a permite ı¸identificarea ˆ mod unic a claselor sale, indiferent de contextul ˆ care acestea ın ınvor fi integrate.
  • 153. 152 CAPITOLUL 6. ORGANIZAREA CLASELOR6.2 Organizarea fi¸ierelor s6.2.1 Organizarea fi¸ierelor surs˘ s aOrice aplicatie nebanal˘ trebuie s˘ fie construit˘ folosind o organizare ier- ¸ a a aarhic˘ a componentelor sale. Este recomandat ca strategia de organizare a afi¸ierelor surs˘ s˘ respecte urm˘toarele conventii: s a a a ¸ • Codul surs˘ al claselor ¸i interfetelor s˘ se gaseasc˘ ˆ fi¸iere ale c˘ror a s ¸ a a ın s a nume s˘ fie chiar numele lor scurt ¸i care s˘ aib˘ extensia .java. a s a a Atentie ¸ Este obligatoriu ca o clas˘/interfat˘ public˘ s˘ se gaseasc˘ ˆ a ¸a a a a ıntr-un fi¸ier avˆnd numele clasei(interfetei) ¸i extenisa .java, sau compilatorul s a ¸ s va furniza o eroare. Din acest motiv, ˆ ıntr-un fi¸ier surs˘ nu pot exista s a dou˘ clase sau interfete publice. Pentru clasele care nu sunt publice a ¸ acest lucru nu este obligatoriu, ci doar recomandat. Intr-un fi¸ier surs˘ s a pot exista oricˆte clase sau interfete care nu sunt publice. a ¸ • Fi¸ierele surs˘ trebuie s˘ se g˘seasc˘ ˆ directoare care s˘ reflecte nu- s a a a a ın a mele pachetelor ˆ care se g˘sesc clasele ¸i interfetele din acele fi¸iere. ın a s ¸ s Cu alte cuvinte, un director va contine surse pentru clase ¸i interfete ¸ s ¸ din acela¸i pachet iar numele directorului va fi chiar numele pachetu- s lui. Dac˘ numele pachetelor sunt formate din mai multe unit˘¸i lexicale a at separate prin punct, atunci acestea trebuie de asemenea s˘ corespund˘ a a unor directoare ce vor descrie calea spre fi¸ierele surs˘ ale c˘ror clase s a a ¸i interfete fac parte din pachetele respective. s ¸ Vom clarifica modalitatea de organizare a fi¸ierelor surs˘ ale unei aplicatii s aprintr-un exemplu concret. S˘ presupunem c˘ dorim crearea unor compo- a anente care s˘ reprezinte diverse notiuni matematice din domenii diferite, a ¸cum ar fi geometrie, algebr˘, analiz˘, etc. Pentru a simplifica lucrurile, s˘ a a apresupunem c˘ dorim s˘ cre˘m clase care s˘ descrie urm˘toarele notiuni: a a a a apoligon, cerc, poliedru, sfer˘, grup, functie. a ¸O prim˘ variant˘ ar fi s˘ construim cˆte o clas˘ pentru fiecare ¸i s˘ le plas˘m a a a a a s a a
  • 154. 6.2. ORGANIZAREA FISIERELOR ¸ 153ˆ acela¸i director ˆın s ımpreuna cu un program care s˘ le foloseasca, ˆ a, avˆnd a ıns˘ aˆ vedere posibila extindere a aplicatiei cu noi reprezent˘ri de notiuni matem-ın ¸ a ¸atice, aceast˘ abordare ar fi ineficient˘. a a O abordare elegant˘ ar fi aceea ˆ care clasele care descriu notiuni din a ın ¸acela¸i domeniu sa se gaseasca ˆ pachete separate ¸i directoare separate. s ın sIerarhia fi¸ierelor sursa ar fi: s/matematica /surse /geometrie /plan Poligon.java Cerc.java /spatiu Poliedru.java Sfera.java /algebra Grup.java /analiza Functie.java Matematica.java Clasele descrise ˆ fi¸ierele de mai sus trebuie declarate ˆ pachete denu- ın s ınmite corespunzator cu numele directoarelor ˆ care se gasesc: ın// Poligon.javapackage geometrie.plan;public class Poligon { . . . }// Cerc.javapackage geometrie.plan;public class Cerc { . . . }// Poliedru.javapackage geometrie.spatiu;public class Poliedru { . . . }
  • 155. 154 CAPITOLUL 6. ORGANIZAREA CLASELOR// Sfera.javapackage geometrie.spatiu;public class Sfera { . . . }// Grup.javapackage algebra;public class Grup { . . . }// Functie.javapackage analiza;public class Functie { . . . } Matematica.java este clasa principal˘ a aplicatiei. a ¸Dup˘ cum se observ˘, numele lung al unei clase trebuie s˘ descrie calea spre a a aacea clas˘ ˆ cadrul fi¸ierelor surs˘, relativ la directorul ˆ care se g˘se¸te a ın s a ın a saplicatia. ¸6.2.2 Organizarea unit˘¸ilor de compilare (.class) atIn urma compil˘rii fi¸ierelor surs˘ vor fi generate unit˘¸i de compilare pentru a s a atfiecare clas˘ ¸i interfat˘ din fi¸ierele surs˘. Dup˘ cum ¸tim acestea au extensia as ¸a s a a s.class ¸i numele scurt al clasei sau interfetei respective. s ¸ Spre deosebire de organizarea surselor, un fi¸ier .class trebuie s˘ se s agaseasca ˆ ıntr-o ierarhie de directoare care s˘ reflecte numele pachetului din acare face parte clasa respectiv˘.a Implicit, ˆ urma compil˘rii fi¸ierele surs˘ ¸i unit˘¸ile de compilare se ın a s a s atg˘sesc ˆ acela¸i director, ˆ a ele pot fi apoi organizate separat. Este reco- a ın s ıns˘mandatˆ a ca aceast˘ separare s˘ fie f˘cut˘ automat la compilare. ıns˘ a a a a Revenind la exemplul de mai sus, vom avea urm˘toarea organizare: a/matematica /clase /geometrie /plan Poligon.class Cerc.class /spatiu
  • 156. 6.2. ORGANIZAREA FISIERELOR ¸ 155 Poliedru.class Sfera.class /algebra Grup.class /analiza Functie.class Matematica.classCrearea acestei structuri ierarhice este facut˘ automat de c˘tre compilator. a aIn directorul aplicatiei (matematica) cre˘m subdirectorul clase ¸i d˘m co- a s amanda: javac -sourcepath surse surse/Matematica.java -d clasesau javac -classpath surse surse/Matematica.java -d clase Optiunea -d specific˘ directorul r˘d˘cin˘ al ierarhiei de clase. In lipsa ¸ a a a alui, fiecare unitate de compilare va fi plasat˘ ˆ acela¸i director cu fi¸ierul s˘u a ın s s asurs˘. aDeoarece compil˘m clasa principal˘ a plicatiei, vor fi compilate ˆ cascad˘ a a ¸ ın atoate clasele referite de aceasta, dar numai acestea. In cazul ˆ care dorim s˘ ın acompil˘m explicit toate fi¸ierele java dintr-un anumit director, de exemplu a ssurse/geometrie/plan, putem folosi expresia: javac surse/geometrie/plan/*.java -d clase6.2.3 Necesitatea organiz˘rii fi¸ierelor a sOrganizarea fi¸ierelor surs˘ este necesar˘ deoarece ˆ momentul cˆnd compi- s a a ın alatorul ˆ alneste un nume de clas˘ el trebuie s˘ poat˘ identifica acea clas˘, ıntˆ a a a aceea ce ˆınseamna c˘ trebuie s˘ gaseasc˘ fi¸erul surs˘ care o contine. a a a s a ¸ Similar, unit˘¸ile de compilare sunt organizate astfel pentru a da posibil- atitatea interpretorului s˘ gaseasc˘ ¸i s˘ ˆ a a s a ıncarce ˆ memorie o anumit˘ clas˘ ˆ ın a a ıntimpul executiei programului. ¸ Ins˘ aceast˘ organizare nu este suficient˘ deoarece specific˘ numai partea a a a afinal˘ din calea c˘tre fi¸ierele .java ¸i .class, de exemplu a a s s/matematica/clase/geometrie/plan/Poligon.class. Pentru aceasta, atˆt ala compilare cˆt ¸i la interpretare trebuie specificat˘ lista de directoare r˘d˘cin˘ a s a a a a
  • 157. 156 CAPITOLUL 6. ORGANIZAREA CLASELORˆ care se g˘sesc fi¸ierele aplicatiei. Aceast˘ list˘ se nume¸te cale de cautareın a s ¸ a a s(classpath). Definitie¸ O cale de c˘utare este o list˘ de directoare sau arhive ˆ care vor fi c˘utate a a ın afi¸ierele necesare unei aplicatii. Fiecare director din calea de cautare este s ¸directorul imediat superior structurii de directoare corespunz˘toare numelor apachetelor ˆ care se g˘sesc clasele din directorul respectiv, astfel ˆ at compi- ın a ıncˆlatorul ¸i interpretorul s˘ poat˘ construi calea complet˘ spre clasele aplicatiei. s a a a ¸Implicit, calea de c˘utare este format˘ doar din directorul curent. a a S˘ consider˘m clasa principal˘ a aplicatiei Matematica.java: a a a ¸import geometrie.plan.*;import algebra.Grup;import analiza.Functie;public class Matematica { public static void main(String args[]) { Poligon a = new Poligon(); geometrie.spatiu.Sfera = new geometrie.spatiu.Sfera(); //... }}Identificarea unei clase referite ˆ program se face ˆ felul urm˘tor: ın ın a • La directoarele aflate ˆ calea de c˘utare se adaug˘ subdirectoarele ın a a specificate ˆ import sau ˆ numele lung al clasei ın ın • In directoarele formate este c˘utat un fi¸ier cu numele clasei. In cazul a s ˆ care nu este g˘sit nici unul sau sunt g˘site mai multe va fi semnalat˘ ın a a a o eroare.6.2.4 Setarea c˘ii de c˘utare (CLASSPATH) a aSetarea c˘ii de c˘utare se poate face ˆ dou˘ modalit˘¸i: a a ın a at • Setarea variabilei de mediu CLASSPATH - folosind aceast˘ variant˘ a a toate aplicatiile Java de pe ma¸ina respectiv˘ vor c˘uta clasele necesare ¸ s a a ˆ directoarele specificate ˆ variabila CLASSPATH. ın ın
  • 158. 6.3. ARHIVE JAR 157 UNIX: SET CLASSPATH = cale1:cale2:... DOS shell (Windows 95/NT/...): SET CLASSPATH = cale1;cale2;... • Folosirea optiunii -classpath la compilarea ¸i interpretarea programelor ¸ s - directoarele specificate astfel vor fi valabile doar pentru comanda curent˘: a javac - classpath <cale de cautare> <surse java> java - classpath <cale de cautare> <clasa principala> Lansarea ˆ executie a aplicatiei noastre, din directorul matematica, se ın ¸va face astfel: java -classpath clase Matematica In concluzie, o organizare eficient˘ a fi¸ierelor aplicatiei ar ar˘ta astfel: a s ¸ a/matematica /surse /clase compile.bat (javac -sourcepath surse surse/Matematica.java -d clase) run.bat (java -classpath clase Matematica)6.3 Arhive JARFi¸ierele JAR (Java Archive) sunt arhive ˆ format ZIP folosite pentru s ınˆımpachetarea aplicatiilor Java. Ele pot fi folosite ¸i pentru comprim˘ri ¸ s aobi¸nuite, diferenta fat˘ de o arhiv˘ ZIP obi¸nuit˘ fiind doar existenta unui s ¸ ¸a a s a ¸director denumit META-INF, ce contine diverse informatii auxiliare legate de ¸ ¸aplicatia sau clasele arhivate. ¸ Un fi¸ier JAR poate fi creat folosind utilitarul jar aflat ˆ distributia s ın ¸J2SDK, sau metode ale claselor suport din pachetul java.util.jar. Dintre beneficiile oferite de arhivele JAR amintim: • portabilitate - este un format de arhivare independent de platform˘; a
  • 159. 158 CAPITOLUL 6. ORGANIZAREA CLASELOR • compresare - dimensiunea unei aplicatii ˆ forma sa final˘ este redus˘; ¸ ın a a • minimizarea timpului de ˆ ıncarcare a unui applet: dac˘ appletul (fi¸iere a s class, resurse, etc) este compresat ˆ ıntr-o arhiv˘ JAR, el poate fi ˆ arcat a ınc˘ ˆ ıntr-o singur˘ tranzactie HTTP, f˘r˘ a fi deci nevoie de a deschide cˆte a ¸ aa a o conexiune nou˘ pentru fiecare fi¸ier; a s • securitate - arhivele JAR pot fi ”semnate” electronic • mecanismul pentru lucrul cu fi¸iere JAR este parte integrata a plat- s formei Java.6.3.1 Folosirea utilitarului jarArhivatorul jar se g˘se¸te ˆ subdirectorul bin al directorului ˆ care este a s ın ıninstalat kitul J2SDK. Mai jos sunt prezentate pe scurt operatiile uzuale: ¸ • Crearea unei arhive jar cf arhiva.jar fi¸ier(e)-intrare s • Vizualizare continutului ¸ jar tf nume-arhiva • Extragerea continutului ¸ jar xf arhiva.jar • Extragerea doar a unor fi¸iere s jar xf arhiva.jar fi¸ier(e)-arhivate s • Executarea unei aplicatii ¸ java -jar arhiva.jar • Deschiderea unui applet arhivat <applet code=A.class archive="arhiva.jar" ...> Exemple: • Arhivarea a dou˘ fi¸iere class: a s jar cf classes.jar A.class B.class • arhivarea tuturor fi¸ierelor din directorul curent: s jar cvf allfiles.jar *
  • 160. 6.3. ARHIVE JAR 1596.3.2 Executarea aplicatiilor arhivate ¸Pentru a rula o aplicatie ˆ ¸ ımpachetat˘ ˆ a ıntr-o arhiv˘ JAR trebuie s˘ facem a acunoscut˘ interpretorului numele clasei principale a aplicatiei. S˘ consider˘m a ¸ a aurm˘torul exemplu, ˆ care dorim s˘ arhiv˘m clasele aplicatiei descrise mai a ın a a ¸sus, ˆ care clasa principal˘ era Matematica.java. Din directorul clase vom ın alansa comanda:jar cvf mate.jar geometrie analiza algebra Matematica.class In urma acestei comenzi vom obtine arhiva mate.jar. Dac˘ vom ˆ a ıncercas˘ lans˘m ˆ executie aceast˘ arhiv˘ prin comanda java -jar mate.jar a a ın ¸ a avom obtine urm˘toarea eroare: ”Failed to load Main-Class manifest from ¸ amate.jar”. Aceasta ˆ ınseamna c˘ ˆ fiserul Manifest.mf ce se gase¸te ˆ di- a ın s ınrectorul META-INF trebuie s˘ ˆ a ınregistr˘m clasa principal˘ a aplicatiei. Acest a a ¸lucru ˆ vom face ˆ doi pa¸i: ıl ın s • se creeaz˘ un fi¸ier cu un nume oarecare, de exemplu manifest.txt, a s ˆ care vom scrie: ın Main-Class: Matematica • adaug˘m aceast˘ informatie la fi¸ierul manifest al arhivei mate.jar: a a ¸ s jar uvfm mate.jar manifest.txt Ambele operatii puteau fi 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 inter-pretorul Java, ceea ce ˆ ınseamn˘ c˘ facˆnd dublu-click pe o arhiv˘ JAR va fi a a a alansat˘ ˆ executie aplicatia ˆ a ın ¸ ¸ ımpachetat˘ ˆ acea arhiv˘ (dac˘ exist˘ o clas˘ a ın a a a aprincipal˘). a
  • 161. 160 CAPITOLUL 6. ORGANIZAREA CLASELOR
  • 162. Capitolul 7Colectii ¸7.1 IntroducereO colectie este un obiect care grupeaz˘ mai multe elemente ˆ ¸ a ıntr-o singur˘aunitate. Prin intermediul colectiilor vom avea acces la diferite tipuri de ¸date cum ar fi vectori, liste ˆ antuite, stive, multimi matematice, tabele de ınl˘ ¸ ¸dispersie, etc. Colectiile sunt folosite atˆt pentru memorarea ¸i manipularea ¸ a sdatelor, cˆt ¸i pentru transmiterea unor informatii de la o metod˘ la alta. a s ¸ a Tipul de date al elementelor dintr-o colectie este Object, ceea ce ˆ ¸ ınseamn˘ ac˘ multimile reprezentate sunt eterogene, putˆnd include obiecte de orice tip. a ¸ a Incepˆnd cu versiunea 1.2, ˆ Java colectiile sunt tratate ˆ a ın ¸ ıntr-o manier˘ aunitar˘, fiind organizate ˆ a ıntr-o arhitectur˘ foarte eficient˘ ¸i flexibil˘ ce cuprinde: a as a • Interfete: tipuri abstracte de date ce descriu colectiile ¸i permit uti- ¸ ¸ s lizarea lor independent de detaliile implement˘rilor. a • Implement˘ri: implement˘ri concrete ale interfetelor ce descriu colectii. a a ¸ ¸ Aceste clase reprezint˘ tipuri de date reutilizabile. a • Algoritmi: metode care efectueaz˘ diverse operatii utile cum ar fi a ¸ c˘utarea sau sortarea, definite pentru obiecte ce implementeaz˘ interfetele a a ¸ ce descriu colectii. Ace¸ti algoritmi se numesc ¸i polimorfici deoarece ¸ s s pot fi folositi pe implement˘ri diferite ale unei colectii, reprezentˆnd ¸ a ¸ a elementul de functionalitate reutilizabil˘. ¸ a Utilizarea colectiilor ofer˘ avantaje evidente ˆ procesul de dezvoltare a ¸ a ınunei aplicatii. Cele mai importante sunt: ¸ 161
  • 163. 162 CAPITOLUL 7. COLECTII ¸ • Reducerea efortului de programare: prin punerea la dispozitia ¸ programatorului a unui set de tipuri de date ¸i algoritmi ce modeleaz˘ s a structuri ¸i operatii des folosite ˆ aplicatii. s ¸ ın ¸ • Cre¸terea vitezei ¸i calit˘¸ii programului: implement˘rile efective s s at a ale colectiilor sunt de ˆ ¸ ınalt˘ performant˘ ¸i folosesc algoritmi cu timp a ¸a s de lucru optim.Astfel, la scrierea unei aplicatii putem s˘ ne concentr˘m eforturile asupra ¸ a aproblemei ˆ sine ¸i nu asupra modului de reprezentare ¸i manipulare a ın s sinformatiilor. ¸7.2 Interfete ce descriu colectii ¸ ¸Interfetele reprezint˘ nucleul mecanismului de lucru cu colectii, scopul lor ¸ a ¸fiind de a permite utilizarea structurilor de date independent de modul lorde implementare. Collection modeleaz˘ o colectie la nivelul cel mai general, descriind un a ¸grup de obiecte numite ¸i elementele sale. Unele implement˘ri ale acestei s ainterfete permit existenta elementelor duplicate, alte implement˘ri nu. Un- ¸ ¸ aele au elementele ordonate, altele nu. Platforma Java nu ofer˘ nici o im- aplementare direct˘ a acestei interfete, ci exist˘ doar implement˘ri ale unor a ¸ a asubinterfete mai concrete, cum ar fi 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);
  • 164. 7.2. INTERFETE CE DESCRIU COLECTII ¸ ¸ 163 // Operatii la nivel de multime boolean containsAll(Collection c); boolean addAll(Collection c); boolean removeAll(Collection c); boolean retainAll(Collection c); // Metode de conversie in vector Object[] toArray(); Object[] toArray(Object a[]);} Set modeleaz˘ notiunea de multime ˆ sens matematic. O multime nu a ¸ ¸ ın ¸poate avea elemente duplicate, mai bine zis nu poate contine dou˘ obiecte o1 ¸ a¸i o2 cu proprietatea o1.equals(o2). Mo¸tene¸te metodele din Collection,s s sf˘r˘ a avea alte metode specifice. aaDou˘ dintre clasele standard care ofer˘ implement˘ri concrete ale acestei a a ainterfete sunt HashSet ¸i TreeSet. ¸ s SortedSet este asem˘n˘toare cu interfata Set, diferenta principal˘ constˆnd a a ¸ ¸ a aˆ faptul c˘ elementele dintr-o astfel de colectie sunt ordonate ascendent.ın a ¸Pune la dispozitie operatii care beneficiaz˘ de avantajul ordon˘rii elementelor. ¸ ¸ a aOrdonarea elementelor se face conform ordinii lor naturale, sau conform cuordinea dat˘ de un comparator specificat la crearea colectiei ¸i este mentinut˘ a ¸ s ¸ aautomat la orice operatie efectuat˘ asupra multimii. Singura condittie este ¸ a ¸ ¸ca, pentru orice dou˘ obiecte o1, o2 ale colectiei, apelul o1.compareT o(o2) a ¸(sau comparator.compare(o1, o2), dac˘ este folosit un comparator) trebuie as˘ fie valid ¸i s˘ nu provoace exceptii. a s a ¸ Fiind subclas˘ a interfetei Set, mo¸tene¸te metodele acesteia, oferind a ¸ s smetode suplimentare ce ¸in cont de faptul c˘ multimea este sortat˘: t a ¸ apublic interface SortedSet extends Set { // Subliste SortedSet subSet(Object fromElement, Object toElement); SortedSet headSet(Object toElement);
  • 165. 164 CAPITOLUL 7. COLECTII ¸ SortedSet tailSet(Object fromElement); // Capete Object first(); Object last(); Comparator comparator();}Clasa care implementeaz˘ aceast˘ interfat˘ este TreeSet. a a ¸a List descrie liste (secvente) de elemente indexate. Listele pot contine ¸duplicate ¸i permit un control precis asupra pozitiei unui element prin in- s ¸termediul indexului acelui element. In plus, fat˘de metodele definite de ¸ainterfata Collection, avem metode pentru acces pozitional, c˘utare ¸i it- ¸ ¸ a serare avansat˘. Definitia interfetei este: a ¸ ¸public interface List extends Collection { // Acces pozitional Object get(int index); Object set(int index, Object element); void add(int index, Object element); Object remove(int index); abstract boolean addAll(int index, Collection c); // Cautare int indexOf(Object o); int lastIndexOf(Object o); // Iterare ListIterator listIterator(); ListIterator listIterator(int index); // Extragere sublista List subList(int from, int to);}
  • 166. 7.2. INTERFETE CE DESCRIU COLECTII ¸ ¸ 165Clase standard care implementeaz˘ aceast˘ interfat˘ sunt: ArrayList, a a ¸aLinkedList, Vector. Map descrie structuri de date ce asociaz˘ fiecarui element o cheie unic˘, a adup˘ care poate fi reg˘sit. Obiectele de acest tip nu pot contine chei dupli- a a ¸cate ¸i fiecare cheie este asociat˘ la un singur element. Ierarhia interfetelor s a ¸derivate din Map este independent˘ de ierarhia derivat˘ din Collection. a aDefinittai interfetei este prezentat˘ mai jos: ¸ ¸ apublic 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); }}
  • 167. 166 CAPITOLUL 7. COLECTII ¸ Clase care implementeaz˘ interfat˘ Map sunt HashMap, TreeMap ¸i a ¸a sHashtable. SortedMap este asem˘n˘toare cu interfata Map, la care se adaug˘ fap- a a ¸ atul c˘ multimea cheilor dintr-o astfel de colectie este mentinut˘ ordonat˘ a ¸ ¸ ¸ a aascendent conform ordinii naturale, sau conform cu ordinea dat˘ de un com- aparator specificat la crearea colectiei. Este subclasa a interfetei Map, oferind ¸ ¸metode suplimentare pentru: extragere de subtabele, aflarea primei/ultimeichei, aflarea comparatorului folosit pentru ordonare.Definitia interfetei este dat˘ mai jos: ¸ ¸ a public interface SortedMap extends Map { // Extragerea de subtabele SortedMap subMap(Object fromKey, Object toKey); SortedMap headMap(Object toKey); SortedMap tailMap(Object fromKey); // Capete Object first(); Object last(); // Comparatorul folosit pentru ordonare Comparator comparator();} Clasa care implementeaz˘ aceast˘ interfat˘ este TreeMap. a a ¸a7.3 Implement˘ri ale colectiilor a ¸Inainte de versiunea 1.2, exista un set de clase pentru lucrul cu colectii, ˆ a ¸ ıns˘acestea nu erau organizate pe ierarhia de interfete prezentat˘ ˆ sectiunea ¸ a ın ¸anterioar˘. Aceste clase sunt ˆ continuare disponibile ¸i multe dintre ele a ın sau fost adaptate ˆ a¸a fel ˆ at s˘ se integreze ˆ noua abordare. Pe lˆng˘ ın s ıncˆ a ın a aacestea au fost create noi clase corespunz˘toare interfetelor definite, chiar a ¸dac˘ functionalitatea lor era aproape identic˘ cu cea a unei clase anterioare. a ¸ a
  • 168. ˘7.3. IMPLEMENTARI ALE COLECTIILOR ¸ 167 Clasele de baz˘ care implementeaz˘ interfete ce descriu colectii au numele a a ¸ ¸de forma < Implementare >< Interf ata >, unde ’implementare’ se refer˘ la astructura intern˘ folosit˘ pentru reprezentarea multimii, ¸i sunt prezentate a a ¸ sˆ tabelul de mai jos, ˆın ımpreun˘ cu interfetele corespunz˘toare (clasele din a ¸ avechiul model sunt trecute pe rˆndul de jos): a Interfata ¸ Clasa Set HashSet SortedSet TreeSet List ArrayList, LinkedList Vector Map HashMap Hashtable SortedMap TreeMap A¸adar se observ˘ existenta unor clase care ofer˘ aceea¸i functionalite, s a ¸ a s ¸cum ar fi ArrayList ¸i Vector, HashMap ¸i Hashtable. s s Pe lˆng˘ organizarea ierarhic˘ a interfetelor implementate, clasele ce de- a a a ¸scriu colectii sunt de asemenea concepute ˆ ¸ ıntr-o manier˘ ierarhic˘, ca ˆ figura a a ınde mai jos:AbstractCollection - AbstractSet, AbstractList - HashSet,TreeSet... Vector-StackAbstractMap - HashMap, TreeMap, HashTableIn vechea ierarhie:Dictionary - Hashtable - PropertiesEvident, implementarea interfetelor este explicit realizat˘ la nivelul super- ¸ aclaselor abstracte, acestea oferind de altfel ¸i implement˘ri concrete pentru s amulte din metodele definite de interfete. ¸ In general, clasele care descriu colectii au unele tr˘saturi comune, cum ar ¸ afi: • permit elementul null, • sunt serializabile, • au definit˘ metoda clone, a
  • 169. 168 CAPITOLUL 7. COLECTII ¸ • au definit˘ metoda toString, care returneaz˘ o reprezentare ca ¸ir de a a s caractere a colectiei respective, ¸ • permit crearea de iteratori pentru parcurgere, • au atˆt constructor f˘r˘ argumente cˆt ¸i un constructor care accept˘ a aa a s a ca argument o alt˘ colectie a ¸ • exceptˆnd clasele din arhitectura veche, nu sunt sincronizate (vezi ”Fire a de executie). ¸7.4 Folosirea eficient˘ a colectiilor a ¸Dup˘ cum am vazut, fiecare interfat˘ ce descrie o colectie are mai multe a ¸a ¸implement˘ri. De exemplu, interfata List este implementat˘ de clasele a ¸ aArrayList ¸i LinkedList, prima fiind ˆ general mult mai folosit˘. De ce s ın aexist˘ atunci ¸i clasa LinkedList ? Raspunsul const˘ ˆ faptul c˘ folosind a s a ın areprezent˘ri diferite ale multimii gestionate putem obtine performante mai a ¸ ¸bune ˆ functie de situatie, prin realizarea unor compromisuri ˆ ın ¸ ¸ ıntre spatiul ¸necesar pentru memorarea datelor, rapiditatea reg˘sirii acestora ¸i timpul a snecesar actualiz˘rii colectiei ˆ cazul unor modific˘ri. a ¸ ın a S˘ consider˘m un exemplu ce creeaza o list˘ folosind ArrayList, respectiv a a aLinkedList ¸i execut˘ diverse operatii pe ea, cronometrˆnd timpul necesar s a ¸ arealiz˘rii acestora: a Listing 7.1: Comparare ArrayList - LinkedListimport java . util .*;public class TestEficienta { final static int N = 100000; public static void testAdd ( List lst ) { long t1 = System . curre ntTimeM illis () ; for ( int i =0; i < N ; i ++) lst . add ( new Integer ( i ) ) ; long t2 = System . curre ntTimeM illis () ; System . out . println ( " Add : " + ( t2 - t1 ) ) ; }
  • 170. ˘7.4. FOLOSIREA EFICIENTA A COLECTIILOR ¸ 169 public static void testGet ( List lst ) { long t1 = System . curre ntTimeMillis () ; for ( int i =0; i < N ; i ++) lst . get ( i ) ; long t2 = System . curre ntTimeMillis () ; System . out . println ( " Get : " + ( t2 - t1 ) ) ; } public static void testRemove ( List lst ) { long t1 = System . currentTimeM illis () ; for ( int i =0; i < N ; i ++) lst . remove (0) ; long t2 = System . currentTimeM illis () ; System . out . println ( " Remove : " + ( t2 - t1 ) ) ; } public static void main ( String args []) { System . out . println ( " ArrayList " ) ; List lst1 = new ArrayList () ; testAdd ( lst1 ) ; testGet ( lst1 ) ; testRemove ( lst1 ) ; System . out . println ( " LinkedList " ) ; List lst2 = new LinkedList () ; testAdd ( lst2 ) ; testGet ( lst2 ) ; testRemove ( lst2 ) ; }} Timpii aproximativi de rulare pe un calculator cu performante medii, ¸exprimati ˆ secunde, sunt dati ˆ tabelul de mai jos: ¸ ın ¸ ın ArrayList LinkedList add 0.12 0.14 get 0.01 87.45 remove 12.05 0.01 A¸adar, ad˘ugarea elementelor este rapid˘ pentru ambele tipuri de liste. s a aArrayList ofer˘ acces ˆ timp constant la elementele sale ¸i din acest motiv a ın sfolosirea lui ”get” este rapid˘, ˆ timp ce pentru LinkedList este extrem a ınde lent˘, deoarece ˆ a ıntr-o list˘ ˆ a ınlantuit˘ accesul la un element se face prin ¸ a
  • 171. 170 CAPITOLUL 7. COLECTII ¸parcurgerea secvential˘ a listei pˆn˘ la elementul respectiv. ¸ a a aLa operatiunea de eliminare, folosirea lui ArrayList este lent˘ deoarece el- ¸ aementele r˘mase sufer˘ un proces de reindexare (shift la stˆnga), ˆ timp a a a ınce pentru LinkedList este rapid˘ ¸i se face prin simpla schimbare a unei a sleg˘turi. Deci, ArrayList se comport˘ bine pentru cazuri ˆ care avem a a ınnevoie de reg˘sirea unor elemente la pozitii diferite ˆ list˘, iar LinkedList a ¸ ın afunctioneaza eficient atunci cˆnd facem multe operatii de modificare (¸tergeri, ¸ a ¸ sinser˘ri). a Concluzia nu este c˘ una din aceste clase este mai ”bun˘” decˆt cealalt˘, a a a aci c˘ exist˘ diferente substantiale ˆ reprezentarea ¸i comportamentul diferitelor a a ¸ ¸ ın simplement˘ri ¸i c˘ alegerea unei anumite clase pentru reprezentarea unei a s amultimi de elemente trebuie s˘ se fac˘ ˆ functie de natura problemei ce ¸ a a ın ¸trebuie rezolvat˘.a7.5 Algoritmi polimorficiAlgoritmii polimorfici descri¸i ˆ aceast˘ sectiune sunt metode definite ˆ s ın a ¸ ınclasa Collections care permit efectuarea unor operatii utile cum ar fi c˘utarea, ¸ asortarea, etc. Caracterisiticile principale ale acestor algoritmi sunt: • sunt metode de clas˘ (statice); a • au un singur argument de tip colectie; ¸ • apelul lor general va fi de forma: Collections.algoritm(colectie, [argumente]); • majoritatea opereaz˘ pe liste dar ¸i pe colectii arbitrare. a s ¸ Metodele mai des folosite din clasa Collections sunt: • sort - sorteaz˘ ascendent o list˘ referitor la ordinea s˘ natural˘ sau la a a a a ordinea dat˘ de un comparator; a • shuffle - amestec˘ elementele unei liste - opusul lui sort; a • binarySearch - efectueaz˘ c˘utarea eficient˘ (binar˘) a unui element a a a a ˆ ıntr-o list˘ ordonat˘; a a
  • 172. 7.6. TIPURI GENERICE 171 • reverse - inverseaz˘ ordinea elementelor dintr-o list˘; a a • fill - populeaza o lista cu un anumit element repetat de un num˘r de a ori; • copy - copie elementele unei liste ˆ alta; ın • min - returneaz˘ minimul dintr-o colectie; a ¸ • max - returneaz˘ maximul dintr-o colectie; a ¸ • swap - interschimb˘ elementele de la dou˘ pozitii specificate ale unei a a ¸ liste; • enumeration - returneaza o enumerare a elementelor dintr-o colectie; ¸ • unmodifiableTipColectie - returneaz˘ o instant˘ care nu poate fi mod- a ¸a ificat˘ a colectiei respective; a ¸ • synchronizedTipColectie - returneaz˘ o instant˘ sincronizat˘ a unei a ¸a a colectii (vezi ”Fire de executie”). ¸ ¸7.6 Tipuri genericeTipurile generice, introduse ˆ versiunea 1.5 a limbajului Java, simplific˘ ın alucrul cu colectii, permitˆnd tipizarea elementelor acestora. Definirea unui ¸ ¸atip generic se realizeaz˘ prin specificarea ˆ a ıntre paranteze unghiulare a unui tipde date Java, efectul fiind impunerea tipului respectiv pentru toate elementelecolectiei: <TipDate>. S˘ consider˘m un exemplu de utilizare a colectiilor ¸ a a ¸ˆınainte ¸i dup˘ introducerea tipurilor generice: s a // Inainte de 1.5 ArrayList list = new ArrayList(); list.add(new Integer(123)); int val = ((Integer)list.get(0)).intValue();In exemplul de mai sus, lista definit˘ poate contine obiecte de orice tip, de¸i a ¸ sam dori ca elementele s˘ fie doar numere ˆ a ıntregi. Mai mult, trebuie s˘ facem acast explicit de la tipul Object la Integer atunci cˆnd prelu˘m valoarea a aunui element. Folosind tipuri generice, putem rescrie secventa astfel: ¸
  • 173. 172 CAPITOLUL 7. COLECTII ¸ // Dupa 1.5, folosind tipuri generice ArrayList<Integer> list = new ArrayList<Integer>(); list.add(new Integer(123)); int val = list.get(0).intValue(); Dac˘ utiliz˘m ¸i mecanismul de autoboxing, obtinem o variant˘ mult sim- a a s ¸ aplificat˘ a secventei initiale: a ¸ ¸ // Dupa 1.5, folosind si autoboxing ArrayList<Integer> list = new ArrayList<Integer>(); list.add(123); int val = list.get(0); In cazul folosirii tipurilor generice, ˆ ıncercarea de a utiliza ˆ cadrul unei ıncolectii a unui element necorespunz˘tor ca tip va produce o eroare la compi- ¸ alare, spre deosebire de varianta anterioar˘ ce permitea doara aruncarea unor aexceptie de tipul ClassCastException ˆ cazul folosirii incorecte a tipurilor. ¸ ın7.7 Iteratori ¸i enumer˘ri s aEnumer˘rile ¸i iteratorii descriu modalit˘¸i pentru parcurgerea secvential˘ a a s at ¸ aunei colectii, indiferent dac˘ aceasta este indexat˘ sau nu. Ei sunt descri¸i ¸ a a sde obiecte ce implementeaz˘ interfetele Enumeration, respectiv Iterator a ¸sau ListIterator. Toate clasele care implementeaz˘ colectii au metode ce a ¸returneaz˘ o enumerare sau un iterator pentru parcurgerea elementelor lor. aDeoarece functionalitatea interfetei Enumeration se reg˘se¸te ˆ Iterator, ¸ ¸ a s ınaceasta din urm˘ este preferat˘ ˆ noile implement˘ri ale colectiilor. a a ın a ¸ Metodele uzuale ale acestor interfete sunt prezentate mai jos, ˆ ¸ ımpreun˘ acu modalitatea lor de folosire, semnificatiile lor fiind 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();
  • 174. ¸ ˘7.7. ITERATORI SI ENUMERARI 173 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)); } Iteratorii simpli permit eliminarea elementului curent din colectia pe care ¸o parcurg, cei de tip ListIterator permit ¸i inserarea unui element la pozitia s ¸curent˘, respectiv modificarea elementului curent, precum ¸i iterarea ˆ am- a s ınbele sensuri. Iteratorii sunt preferati enumer˘rilor datorit˘ posibilit˘¸ii lor ¸ a a atde a actiona asupra colectiei pe care o parcurg prin metode de tip remove, ¸ ¸add, set dar ¸i prin faptul c˘ denumirile metodelor sunt mai concise. s a Atentie¸ Deoarece colectiile sunt construite peste tipul de date Object, metodele ¸de tip next sau prev ale iteratorilor vor returna tipul Object, fiind respon-sabilitatea noastr˘ de a face conversie (cast) la alte tipuri de date, dac˘ este a acazul.
  • 175. 174 CAPITOLUL 7. COLECTII ¸ In exemplul de mai jos punem ˆ ıntr-un vector numerele de la 1 la 10, leamestec˘m, dup˘ care le parcurgem element cu element folosind un iterator, a aˆınlocuind numerele pare cu 0. Listing 7.2: Folosirea unui iteratorimport 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ˆnd cu versiunea 1.5 a limbajului Java, exist˘ o variant˘ simplificat˘ a a a ade utilizare a iteratorilor. Astfel, o secvent˘ de genul: ¸a ArrayList<Integer> list = new ArrayList<Integer>(); for (Iterator i = list.iterator(); i.hasNext();) { Integer val=(Integer)i.next(); // Proceseaza val ...
  • 176. ¸ ˘7.7. ITERATORI SI ENUMERARI 175 } poate fi rescris˘ astfel: a ArrayList<Integer> list = new ArrayList<Integer>(); for (Integer val : list) { // Proceseaza val ... }
  • 177. 176 CAPITOLUL 7. COLECTII ¸
  • 178. Capitolul 8Serializarea obiectelor8.1 Folosirea serializ˘rii aDefinitie¸ Serializarea este o metod˘ ce permite transformarea unui obiect ˆ a ıntr-osecventa de octeti sau caractere din care s˘ poat˘ fi ref˘cut ulterior obiectul ¸˘ ¸ a a aoriginal. Cu alte cuvinte, serializarea permite salvarea ˆ ıntr-o manier˘ unitar˘ a a atuturor informatiilor unui obiect pe un mediu de stocare extern programului. ¸Procesul invers, de citire a unui obiect serializat pentru a-i reface stareaoriginal˘, se nume¸te deserializare. Intr-un cadru mai larg, prin serializare a sse ˆ ıntelege procesul de scriere/citire a obiectelor. Tipurile primitive pot fi de asemenea serializate. Utilitatea serializarii const˘ ˆ urm˘toarele aspecte: a ın a • Asigur˘ un mecanism simplu de utilizat pentru salvarea ¸i restaurarea a s a datelor. • Permite persistenta obiectelor, ceea ce ˆ ¸ ınseamna c˘ durata de viata a a ¸ unui obiect nu este determinat˘ de executia unui program ˆ care acesta a ¸ ın este definit - obiectul poate exista ¸i ˆ s ıntre apelurile programelor care ˆ ıl folosesc. Acest lucru se realizeaz˘ prin serializarea obiectului ¸i scrierea a s lui pe disc ˆınainte de terminarea unui program, apoi, la relansarea programului, obiectul va fi citit de pe disc ¸i starea lui refacut˘. Acest s a 177
  • 179. 178 CAPITOLUL 8. SERIALIZAREA OBIECTELOR tip de persistent˘ a obiectelor se nume¸te persistent˘ u¸oar˘, ˆ ¸a s ¸a s a ıntrucˆt a ea trebuie efectuat˘ explicit de c˘tre programator ¸i nu este realizat˘ a a s a automat de c˘tre sistem. a • Compensarea diferentelor ˆ ¸ ıntre sisteme de operare - transmiterea unor informatii ˆ ¸ ıntre platforme de lucru diferite se realizeaz˘ unitar, inde- a pendent de formatul de reprezentare a datelor, ordinea octetilor sau ¸ alte detalii specifice sistemelor repective. • Transmiterea datelor ˆ retea - Aplicatiile ce ruleaz˘ ˆ retea pot comu- ın ¸ ¸ a ın ¸ nica ˆ ıntre ele folosind fluxuri pe care sunt trimise, respectiv receptionate ¸ obiecte serializate. • RMI (Remote Method Invocation) - este o modalitate prin care metodele unor obiecte de pe o alt˘ ma¸in˘ pot fi apelate ca ¸i cum acestea ar ex- a s a s ista local pe ma¸ina pe care ruleaz˘ aplicatia. Atunci cˆnd este trimis s a ¸ a un mesaj c˘tre un obiect ”remote” (de pe alt˘ ma¸in˘), serializarea a a s a este utilizat˘ pentru transportul argumentelor prin retea ¸i pentru re- a ¸ s turnarea valorilor. • Java Beans - sunt componente reutilizabile, de sine st˘t˘toare ce pot aa fi utilizate ˆ medii vizuale de dezvoltare a aplicatiilor. Orice compo- ın ¸ nent˘ Bean are o stare definit˘ de valorile implicite ale propriet˘¸ilor a a at sale, stare care este specificat˘ ˆ etapa de design a aplicatiei. Mediile a ın ¸ vizuale folosesc mecanismul serializ˘rii pentru asigurarea persistentei a ¸ componentelor Bean. Un aspect important al serializ˘rii este c˘ nu salveaz˘ doar imaginea unui a a aobiect ci ¸i toate referintele la alte obiecte pe care acesta le contine. Acesta s ¸ ¸este un proces recusiv de salvare a datelor, ˆıntrucˆt celelalte obiectele referite ade obiectul care se serializeaz˘ pot referi la rˆndul lor alte obiecte, ¸i a¸a mai a a s sdeparte. A¸adar referintele care construiesc starea unui obiect formeaz˘ o s ¸ aˆıntreag˘ retea, ceea ce ˆ a ¸ ınseamn˘ c˘ un algoritm general de salvare a st˘rii a a aunui obiect nu este tocmai facil. In cazul ˆ care starea unui obiect este format˘ doar din valori ale unor ın avariabile de tip primitiv, atunci salvarea informatiilor ˆ ¸ ıncapsulate ˆ acel ınobiect se poate face ¸i prin salvarea pe rˆnd a datelor, folosind clasa s aDataOutputStream, pentru ca apoi s˘ fie restaurate prin metode ale clasei aDataInputStream, dar, a¸a cum am vazut, o asemenea abordare nu este s
  • 180. ˘8.1. FOLOSIREA SERIALIZARII 179ˆ general suficient˘, deoarece pot ap˘rea probleme cum ar fi: variabileleın a amembre ale obiectului pot fi instante ale altor obiecte, unele cˆmpuri pot ¸ aface referint˘ la acela¸i obiect, etc. ¸a s Serializarea ˆ format binar a tipurilor primitive ¸i a obiectelor se real- ın sizeaz˘ prin intermediul fluxurilor definite de clase specializate ˆ acest scop a ıncu ar fi:ObjectOutputStream pentru scriere ¸i ObjectInputStream pentru restau- srare. In continuare, prin termenul serializare ne vom referi doar la serializareaˆ format binar.ın8.1.1 Serializarea tipurilor primitiveSerializarea tipurilor primitive poate fi realizat˘ fie prin intermediul fluxu- arilor DataOutputStream ¸i DataInputStream, fie cu ObjectOutputStream s¸i ObjectInputStream. Acestea implementeaz˘ interfetele DataInput, re-s a ¸spectiv DataOutput ce declar˘ metode de tipul readTipPrimitiv, respectiv awriteTipPrimitiv pentru scrierea/citirea datelor primitive ¸i a ¸irurilor de s scaractere. Mai jos este prezentat un exemplu de serializare folosind clasa DataOutputStream: FileOutputStream fos = new FileOutputStream("test.dat"); DataOutputStream out = new DataOutputStream(fos); out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); out.flush(); fos.close(); Citirea informatiilor scrise ˆ exemplul de mai sus se va face astfel: ¸ ın FileInputStream fis = new FileInputStream("test.dat"); DataInputStream in = new DataInputStream(fis); int i = in.readInt(); double d = in.readDouble(); boolean b = in.readBoolean();
  • 181. 180 CAPITOLUL 8. SERIALIZAREA OBIECTELOR String s = in.readUTF(); fis.close();8.1.2 Serializarea obiectelorSerializarea obiectelor se realizeaz˘ prin intermediul fluxurilor definite de aclasele ObjectOutputStream (pentru salvare) ¸i ObjectInputStream s(pentru restaurare). Acestea sunt fluxuri de procesare, ceea ce ˆ ınseamnac˘ vor fi folosite ˆ a ımpreuna cu alte fluxuri pentru scrierea/citirea efectiv˘ a adatelor pe mediul extern pe care va fi salvat, sau de pe care va fi restauratun obiect serializat. Mecanismul implicit de serializare a unui obiect va salva numele claseiobiectului, signatura clasei ¸i valorile tuturor cˆmpurile serializabile ale obiec- s atului. Referintele la alte obiecte serializabile din cadrul obiectului curent vor ¸duce automat la serializarea acestora iar referintele multiple c˘tre un acela¸i ¸ a sobiect sunt codificate utilizˆnd un algoritm care s˘ poat˘ reface ”reteaua de a a a ¸obiecte” la aceea¸i stare ca atunci cˆnd obiectul original a fost salvat. s a Clasele ObjectInputStream ¸i ObjectOutputStream implementeaz˘ interfetele s a ¸ObjectInput, respectiv ObjectOutput care extind DataInput, respectiv DataOutput,ceea ce ˆ ınseamn˘ c˘, pe lˆng˘ metodele dedicate serializ˘rii obiectelor, vor a a a a aexista ¸i metode pentru scrierea/citirea datelor primitive ¸i a ¸irurilor de s s scaractere. Metodele pentru serializarea obiectelor sunt: • writeObject, pentru scriere ¸i s • readObject, pentru restaurare.8.1.3 Clasa ObjectOutputStreamScrierea obiectelor pe un flux de ie¸ire este un proces extrem de simplu, ssecventa uzual˘ fiind cea de mai jos: ¸ a ObjectOutputStream out = new ObjectOutputStream(fluxPrimitiv); out.writeObject(referintaObiect); out.flush(); fluxPrimitiv.close();
  • 182. ˘8.1. FOLOSIREA SERIALIZARII 181 Exemplul de mai jos construie¸te un obiect de tip Date ¸i ˆ salveaz˘ ˆ s s ıl a ınfi¸ierul test.ser, ˆ s ımpreun˘ cu un obiect de tip String. Evident, fi¸ierul a srezultat va contine informatiile reprezentate ˆ format binar. ¸ ¸ ın FileOutputStream fos = new FileOutputStream("test.ser"); ObjectOutputStream out = new ObjectOutputStream(fos); out.writeObject("Ora curenta:"); out.writeObject(new Date()); out.flush(); fos.close(); Deoarece implementeaz˘ interfata DataOutput, pe lˆnga metoda de scriere a ¸ aa obiectelor, clasa pune la dispozitie ¸i metode de tipul writeTipPrimitiv spentru serializarea tipurilor de date primitive ¸i a ¸irurilor de caractere, ast- s sfel ˆ at apeluri ca cele de mai jos sunt permise : ıncˆ out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); Metoda writeObject arunc˘ exceptii de tipul IOException ¸i derivate a ¸ sdin aceasta, mai precis NotSerializableException dac˘ obiectul primit ca aargument nu este serializabil, sau InvalidClassException dac˘ sunt prob- aleme cu o clas˘ necesar˘ ˆ procesul de serializare. Vom vedea ˆ continuare a a ın ınc˘ un obiect este serializabil dac˘ este instant˘ a unei clase ce implementeaz˘ a a ¸a ainterfata Serializable. ¸8.1.4 Clasa ObjectInputStreamOdat˘ ce au fost scrise obiecte ¸i tipuri primitive de date pe un flux, citirea a sacestora ¸i reconstruirea obiectelor salvate se va face printr-un flux de intrare sde tip ObjectInputStream. Acesta este de asemenea un flux de procesare¸i va trebui asociat cu un flux pentru citirea efectiv˘ a datelor, cum ar fis aFileInputStream pentru date salvate ˆ ıntr-un fi¸ier. Secventa uzual˘ pentru s ¸ adeserializare este cea de mai jos: ObjectInputStream in = new ObjectInputStream(fluxPrimitiv); Object obj = in.readObject(); //sau
  • 183. 182 CAPITOLUL 8. SERIALIZAREA OBIECTELOR TipReferinta ref = (TipReferinta)in.readObject(); fluxPrimitiv.close(); Citirea informatiilor scrise ˆ exemplul de mai sus se va face astfel: ¸ ın FileInputStream fis = new FileInputStream("test.ser"); ObjectInputStream in = new ObjectInputStream(fis); String mesaj = (String)in.readObject(); Date data = (Date)in.readObject(); fis.close(); Trebuie observat c˘ metoda readObject are tipul returnat Object, ceea ace ˆ ınseamn˘ c˘ trebuie realizat˘ explicit conversia la tipul corespunzator a a aobiectului citit: Date date = in.readObject(); // gresit Date date = (Date)in.readObject(); // corect Atentie¸ Ca ¸i la celelalte fluxuri de date care implemeteaz˘ interfata DataInput s a ¸citirea dintr-un flux de obiecte trebuie s˘ se fac˘ exact ˆ ordinea ˆ carea a a ın ınacestea au fost scrise, altfel vor ap˘rea evident exceptii ˆ procesul de dese- a ¸ ınrializare. Clasa ObjectInputStream implementeaz˘ interfata DataInput deci, pe a ¸lˆng˘ metoda de citire a obiectelor, clasa pune la dispozitie ¸i metode de a a ¸ stipul readTipPrimitiv pentru citirea tipurilor de date primitive ¸i a ¸irurilor s sde caractere. int i = in.readInt(); double d = in.readDouble(); boolean b = in.readBoolean(); String s = in.readUTF();
  • 184. 8.2. OBIECTE SERIALIZABILE 1838.2 Obiecte serializabileUn obiect este serializabil dac˘ ¸i numai dac˘ clasa din care face parte im- as aplementeaz˘ interfata Serializable. A¸adar, dac˘ dorim ca instantele unei a ¸ s a ¸clase s˘ poat˘ fi serializate, clasa respectiv˘ trebuie s˘ implementeze, direct a a a asau indirect, interfata Serializable. ¸8.2.1 Implementarea interfetei Serializable ¸Interfata Serializable nu contine nici o declaratie de metod˘ sau constant˘, ¸ ¸ ¸ a asingurul ei scop fiind de a identifica clasele ale c˘ror obiecte sunt serializabile. aDefinitia sa complet˘ este: ¸ apackage java.io;public interface Serializable { // Nimic !} Declararea claselor ale c˘ror instante trebuie s˘ fie serializate este a¸adar a ¸ a sextrem de simpl˘, fiind f˘cut˘ prin simpla implementare a interfetei Serializable: a a a ¸public class ClasaSerializabila implements Serializable { // Corpul clasei} Orice subclas˘ a unei clase serializabile este la rˆndul ei serializabil˘, a a aˆıntrucˆt implementeaz˘ indirect interfata Serializable. a a ¸ In situatia ˆ care dorim s˘ declar˘m o clas˘ serializabil˘ dar superclasa ¸ ın a a a asa nu este serializabil˘, atunci trebuie s˘ avem ˆ vedere urm˘toarele lucruri: a a ın a • Variabilele accesibile ale superclasei nu vor fi serializate, fiind respons- abilitatea clasei curente de a asigura un mecanism propriu pentru sal- varea/restaurarea lor. Acest lucru va fi discutat ˆ sectiunea referitoare ın ¸ la personalizarea serializ˘rii. a • Superclasa trebuie s˘ aib˘ obligatoriu un constructor accesibil f˘r˘ ar- a a aa gumente, acesta fiind utilizat pentru initializarea variabilelor mo¸tenite ¸ s ˆ procesul de restaurare al unui obiect. Variabilele proprii vor fi ın initializate cu valorile de pe fluxul de intrare. In lipsa unui constructor ¸ accesibil f˘r˘ argumente pentru superclas˘, va fi generat˘ o exceptie la aa a a ¸ executie. ¸
  • 185. 184 CAPITOLUL 8. SERIALIZAREA OBIECTELOR In procesul serializ˘rii, dac˘ este ˆ alnit un obiect care nu implementeaz˘ a a ıntˆ ainterfata Serializable atunci va fi generat˘ o exceptie de tipul NotSerializableException ¸ a ¸ce va identifica respectiva clas˘ neserializabil˘. a a8.2.2 Controlul serializ˘rii aExist˘ cazuri cˆnd dorim ca unele variabile membre ale unui obiect s˘ nu fie a a asalvate automat ˆ procesul de serializare. Acestea sunt cazuri comune atunci ıncˆnd respectivele cˆmpuri reprezint˘ informatii confidentiale, cum ar fi pa- a a a ¸ ¸role, sau variabile temporare pe care nu are rost s˘ le salv˘m. Chiar declarate a aprivate ˆ cadrul clasei aceste cˆmpuri particip˘ la serializare. Pentru ca un ın a acˆmp s˘ nu fie salvat ˆ procesul de serializare el trebuie declarat cu modi- a a ınficatorul transient ¸i trebuie s˘ fie ne-static. De exemplu, declararea unei s avariabile membre temporare ar trebui facut˘ astfel: a transient private double temp; // Ignorata la serializare Modificatorul static anuleaz˘ efectul modificatorului transient. Cu aalte cuvinte, variabilele de clas˘ particip˘ obligatoriu la serializare. a a static transient int N; // Participa la serializare In exemplele urm˘toare cˆmpurile marcate ’DA’ particip˘ la serializare, a a acele marcate ’NU’, nu particip˘ iar cele marcate cu ’Exceptie’ vor provoca aexceptii de tipul NotSerializableException. ¸ Listing 8.1: Modificatorii static ¸i transient simport java . io .*;public class Test1 implements Serializable { int x =1; // DA transient int y =2; // NU transient static int z =3; // DA static int t =4; // DA public String toString () { return x + " , " + y + " , " + z + " , " + t ; }}
  • 186. 8.2. OBIECTE SERIALIZABILE 185 Dac˘ un obiect ce trebuie serializat are referinte la obiecte neserializabile, a ¸atunci va fi generat˘ o exceptie de tipul NotSerializableException. a ¸ Listing 8.2: Membrii neserializabiliimport 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ˆnd o clas˘ serializabila deriva dintr-o alt˘ clas˘, salvarea cˆmpurilor a a a a aclasei p˘rinte se va face doar dac˘ ¸i aceasta este serializabil˘. In caz contrar, a as asubclasa trebuie s˘ salveze explicit ¸i cˆmpurile mo¸tenite. a s a s Listing 8.3: Serializarea cˆmpurilor mo¸tenite a simport 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
  • 187. 186 CAPITOLUL 8. SERIALIZAREA OBIECTELOR } public String toString () { return x + " , " + y ; }} Mai jos este descrisa o aplicatie care efectueaz˘ salvarea ¸i restaurarea ¸ a sunor obiecte din cele trei clase prezentate mai sus. Listing 8.4: Testarea serializ˘rii aimport java . io .*;public class Exemplu { public static void test ( Object obj ) throws IOException { // Salvam FileOutputStream fos = new FileOutputStream ( " fisier . ser " ) ; ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ; out . writeObject ( obj ) ; out . flush () ; fos . close () ; System . out . println ( " A fost salvat obiectul : " + obj ) ; // Restauram FileInputStream fis = new FileInputStream ( " fisier . ser " ) ; ObjectInputStream in = new Obje ctInput Stream ( fis ) ; try { obj = in . readObject () ; } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } fis . close () ; System . out . println ( " A fost restaurat obiectul : " + obj ) ; } public static void main ( String args []) throws IOException { test ( new Test1 () ) ; try { test ( new Test2 () ) ; } catch ( N o t Se r i a l i z a b l e E x c e p t i o n e ) {
  • 188. ˘8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR 187 System . out . println ( " Obiect neserializabil : " + e ) ; } test ( new Test3 () ) ; }} Rezultatul acestui program va fi :A fost salvat obiectul: 1, 2, 3, 4A fost restaurat obiectul: 1, 0, 3, 4Obiect neserializabil: java.io.NotSerializableException: AA fost salvat obiectul: 1, 2A fost restaurat obiectul: 0, 28.3 Personalizarea serializ˘rii obiectelor aDezavantajul mecanismului implicit de serializare este c˘ algoritmul pe care ase beazeaz˘, fiind creat pentru cazul general, se poate comporta ineficient ˆ a ınanumite situatii: poate fi mult mai lent decˆt este cazul sau reprezentarea ¸ abinar˘ generat˘ pentru un obiect poate fi mult mai voluminoas˘ decˆt ar a a a atrebui. In aceste situatii, putem s˘ ˆ ¸ a ınlocuim algoritmul implicit cu unulpropriu, particularizat pentru o clas˘ anume. De asemenea, este posibil as˘ extindem comportamentul implicit, ad˘ugˆnd ¸i alte informatii necesare a a a s ¸pentru serializarea unor obiecte.In majoritatea cazurilor mecanismul standard este suficient ˆ a, dup˘ cum ıns˘ aam spus, o clas˘ poate avea nevoie de mai mult control asupra serializ˘rii. a a Personalizarea serializarii se realizeaz˘ prin definirea (ˆ a ıntr-o clas˘ serial- aizabil˘!) a metodelor writeObject ¸i readObject avˆnd exact signatura de a s amai jos: private void writeObject(java.io.ObjectOutputStream stream) throws IOException private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException Metoda writeObject controleaz˘ ce date sunt salvate iar readObject acontroleaz˘ modul ˆ care sunt restaurate obiectele, citind informatiile salvate a ın ¸¸i, eventual, modifcˆnd starea obiectelor citite astfel ˆ at ele s˘ corespund˘s a ıncˆ a a
  • 189. 188 CAPITOLUL 8. SERIALIZAREA OBIECTELORanumitor cerinte. In cazul ˆ care nu dorim s˘ ˆ ¸ ın a ınlocuim complet mecanis-mul standard, putem s˘ folosim metodele defaultWriteObject, respectiv adefaultReadObject care descriu procedurile implicite de serializare. Forma general˘ de implementare a metodelor writeObject ¸i readObject a seste:private void writeObject(ObjectOutputStream stream) throws IOException { // Procesarea campurilor clasei (criptare, etc.) ... // Scrierea obiectului curent stream.defaultWriteObject(); // Adaugarea altor informatii suplimentare ...}private void readObject(ObjectInputStream stream) throws IOException,ClassNotFoundException { // Restaurarea obiectului curent stream.defaultReadObject(); // Actualizarea starii obiectului (decriptare, etc.) // si extragerea informatiilor suplimentare ...} Metodele writeObject ¸i readObject sunt responsabile cu serializarea sclasei ˆ care sunt definite, serializarea superclasei sale fiind facut˘ automat ın a(¸i implicit). Dac˘ ˆ a o clas˘ trebuie sa-¸i coordoneze serializarea pro- s a ıns˘ a sprie cu serializarea superclasei sale, atunci trebuie s˘ implementeze interfata a ¸Externalizable.8.3.1 Controlul versiunilor claselorS˘ presupunem c˘ dorim s˘ realiz˘m o aplicatie care s˘ ¸in˘ evidenta angajatilor a a a a ¸ at a ¸ ¸unei companii. Evident, vom avean nevoie de o clas˘ care s˘ reprezinte a a
  • 190. ˘8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR 189notiunea de angjat. O variant˘ simplificat˘ a acesteia ar putea ar˘ta astfel: ¸ a a a Listing 8.5: Prima variant˘ a clasei Angajat aimport java . io .*;class Angajat implements Serializable { public String nume ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; }} Mai jos este prezentat˘ o mic˘ aplicatie care permite introducerea de a a ¸angajati ¸i salvarea lor ˆ ¸ s ıntr-un fi¸ier. La fiecare pornire a aplicatiei, vor fi s ¸citite datele din fi¸ier astfel ˆ at programul va actualiza ˆ permanent˘ lista s ıncˆ ın ¸aangajatilor cu noi persoane. ¸ Listing 8.6: Aplicatia de gestionare a angajatilor ¸ ¸import java . io .*;import java . util .*;public class GestiuneAngajati { // Lista angajatilor ArrayList ang = new ArrayList () ; public void citire () throws IOException { FileInputStream fis = null ; try { fis = new FileInputStream ( " angajati . ser " ) ; ObjectInputStream in = new Obje ctInput Stream ( fis ) ;
  • 191. 190 CAPITOLUL 8. SERIALIZAREA OBIECTELOR ang = ( ArrayList ) in . readObject () ; } catch ( File NotF o u n d E x c e p t i o n e ) { System . out . println ( " Fisierul nou ... " ) ; } catch ( Exception e ) { System . out . println ( " Eroare la citirea datelor ... " ) ; e . printStackTrace () ; } finally { if ( fis != null ) fis . close () ; } System . out . println ( " Lista angajatilor : n " + ang ) ; } public void salvare () throws IOException { FileOutputStream fos = new FileOutputStream ( " angajati . ser " ) ; ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ; out . writeObject ( ang ) ; } public void adaugare () throws IOException { BufferedReader stdin = new BufferedReader ( new InputStream Reader ( System . in ) ) ; while ( true ) { System . out . print ( " nNume : " ) ; String nume = stdin . readLine () ; System . out . print ( " Salariu : " ) ; int salariu = Integer . parseInt ( stdin . readLine () ) ; System . out . print ( " Parola : " ) ; String parola = stdin . readLine () ; ang . add ( new Angajat ( nume , salariu , parola ) ) ; System . out . print ( " Mai adaugati ? ( D / N ) " ) ; String raspuns = stdin . readLine () . toUpperCase () ; if ( raspuns . startsWith ( " N " ) ) break ; } } public static void main ( String args []) throws IOException { GestiuneAngajati app = new GestiuneAngajati () ;
  • 192. ˘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˘toarea. Dup˘ introducerea unui a anum˘r suficient de mare de angajati ˆ fi¸ier, clasa Angajat este modifi- a ¸ ın scat˘ prin ad˘ugarea unei noi variabil˘ membre care s˘ retina adresa. La a a a a ¸executia aplicatiei noastre, procedura de citire a angajatilor din fi¸ier nu ¸ ¸ ¸ sva mai functiona, producˆnd o exceptie de tipul InvalidClassException. ¸ a ¸Aceast˘problem˘ ar fi ap˘rut chiar dac˘ variabila ad˘ugat˘ era declarat˘ de a a a a a a atip transient. De ce se ˆ amplˆ acest lucru ? ıntˆ a Explicatia const˘ ˆ faptul c˘ mecanismul de serializare Java este foarte ¸ a ın aatent cu signatura claselor serializate. Pentru fiecare obiect serializat estecalculat automat un num˘r reprezentat pe 64 de biti, care reprezint˘ un fel de a ¸ a”amprent˘” a clasei obiectului. Acest num˘r, denumit serialVersionUID, a aeste generat pornind de la diverse informatii ale clasei, cum ar fi variabilele ¸sale membre, (dar nu numai) ¸i este salvat ˆ procesul de serializare ˆ s ın ımpreun˘ acu celelalte date. In plus, orice modificare semnificativ˘ a clasei, cum ar afi ad˘ugarea unui nou cˆmp, va determina modificarea num˘rului s˘u de a a a aversiune. La restaurarea unui obiect, num˘rul de versiune salvat ˆ forma serializat˘ a ın ava fi reg˘sit ¸i comparat cu noua semn˘tur˘ a clasei obiectului. In cazul ˆ a s a a ıncare acestea nu sunt egale, va fi generat˘ o exceptie de tipul a ¸InvalidClassException ¸i deserializarea nu va fi f˘cut˘. s a a Aceast˘ abordare extrem de precaut˘ este foarte util˘ pentru prevenirea a a aunor anomalii ce pot ap˘rea cˆnd dou˘ versiuni de clase sunt incompati- a a abile, dat poate fi sup˘r˘toare atunci cˆnd modific˘rile aduse clasei nu stric˘ aa a a acompatibilitatea cu vechea versiune. In aceast˘ situatie trebuie s˘ comu- a ¸ anic˘m explicit c˘ cele dou˘ clase sunt compatibile. Acest lucru se realizeaz˘ a a a aprin setarea manual˘ a variabilei serialVersionUID ˆ cadrul clasei dorite, a ınad˘ugˆnd pur ¸i simplu cˆmpul: a a s a
  • 193. 192 CAPITOLUL 8. SERIALIZAREA OBIECTELORstatic final long serialVersionUID = /* numar_serial_clasa */; Prezenta variabilei serialVersionUID printre membrii unei clase va in- ¸forma algoritmul de serialzare c˘ nu mai calculeze num˘rul de serie al clasei, a aci s˘-l foloseasc˘ pe cel specificat de noi. Cum putem afla ˆ a num˘rul de se- a a ıns˘ arie al vechii clase Angajat care a fost folosit˘ anterior la salvarea angajatilor a ¸? Utilitarul serialVer permite generarea num˘rului serialVersionUID pen- atru o clas˘ specificat˘. A¸adar, trebuie s˘ recompil˘m vechea clas˘ Angajat a a s a a a¸i s˘-i afl˘m num˘rul de serie astfel: serialVer Angajat. Rezultatul va fi:s a a aAngajat:static final long serialVersionUID = 5653493248680665297L; Vom rescrie noua clas˘ Angajat astfel ˆ at s˘ fie compatibil˘ cu cea a ıncˆ a aveche astfel: Listing 8.7: Variant˘ compatibil˘ a clasei Angajat a aimport java . io .*;class Angajat implements Serializable { static final long serialVersionUID = 56 534 93 248 680 665 29 7 L ; public String nume , adresa ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . adresa = " Iasi " ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; }} Aplicatia noastr˘ va functiona acum, ˆ a rubrica adres˘ nu va fi initializat˘ ¸ a ¸ ıns˘ a ¸ aˆ nici un fel (va fi null), deoarece ea nu exista ˆ formatul original. La nouaın ın
  • 194. ˘8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR 193salvare a datelor, vor fi serializate ¸i informatiile legate de adres˘ (evident, s ¸ atrebuie ˆ a s˘ le citim de la tastatur˘...) ıns˘ a a8.3.2 Securizarea datelorDup˘ cum am v˘zut membrii privati, cum ar fi parola din exemplul de mai a a ¸sus, particip˘ la serializare. Problema const˘ ˆ faptul c˘, de¸i ˆ format a a ın a s ınbinar, informatiile unui obiect serializat nu sunt criptate ˆ nici un fel ¸i pot ¸ ın sfi reg˘site cu u¸urint˘, ceea ce poate reprezenta un inconvenient atunci cˆnd a s ¸a aexist˘ cˆmpuri confidentiale. a a ¸ Rezolvarea acestei probleme se face prin modificarea mecanismului im-plicit de serializare, implementˆnd metodele readObject ¸i writeObject, a sprecum ¸i prin utilizarea unei functii de criptare a datelor. Varianta secur- s ¸izat˘ a clasei Angajat din exemplul anterior ar putea ar˘ta astfel: a a Listing 8.8: Varianta securizat˘ a clasei Angajat aimport java . io .*;class Angajat implements Serializable { static final long serialVersionUID = 5 65 349 324 868 06 652 97 L ; public String nume , adresa ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . adresa = " Iasi " ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } static String criptare ( String input , int offset ) { StringBuffer sb = new StringBuffer () ; for ( int n =0; n < input . length () ; n ++) sb . append (( char ) ( offset + input . charAt ( n ) ) ) ; return sb . toString () ;
  • 195. 194 CAPITOLUL 8. SERIALIZAREA OBIECTELOR } private void writeObject ( Ob je ct Ou tpu tS tr ea m stream ) throws IOException { parola = criptare ( parola , 3) ; stream . defaultWri te Ob je ct () ; parola = criptare ( parola , -3) ; } private void readObject ( O bjectIn putStre am stream ) throws IOException , C l a s s N o t F o u n d E x c e p t i o n { stream . defaultRead Object () ; parola = criptare ( parola , -3) ; }}8.3.3 Implementarea interfetei Externalizable ¸Pentru un control complet, explicit, al procesului de serializare, o clas˘ tre- abuie s˘ implementeze interfata Externalizable. Pentru instante ale acestor a ¸ ¸clase doar numele clasei este salvat automat pe fluxul de obiecte, clasa fiindresponsabil˘ cu scrierea ¸i citirea membrilor s˘i ¸i trebuie s˘ se coordoneze a s a s acu superclasele ei. Definitia interfetei Externalizable este: ¸ ¸package java.io;public interface Externalizable extends Serializable { public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;} A¸adar, aceste clase trebuie s˘ implementeze obligatoriu metodele write- s aExternal ¸i readExternal ˆ care se va face serializarea complet˘ a obiectelor s ın a¸i coordonarea cu superclasa ei.s Uzual, interfata Externalizable este folosit˘ ˆ situatii ˆ care se dore¸te ¸ a ın ¸ ın sˆımbun˘t˘¸irea performantelor algoritmului standard, mai exact cre¸terea vitezei a at ¸ sprocesului de serializare.
  • 196. ˘8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR 195 Mai jos este prezentat˘ o clas˘ simpl˘ ¸i modalitatea de rescriere a sa a a a sfolosind interfata Externalizable: ¸ Listing 8.9: Serializare implicit˘ aimport 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 proprieimport 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 () ; }}
  • 197. 196 CAPITOLUL 8. SERIALIZAREA OBIECTELOR8.4 Clonarea obiectelorSe ¸tie c˘ nu putem copia valoarea unui obiect prin instructiunea de atribuire. s a ¸O secventa de forma: ¸ TipReferinta o1 = new TipReferinta(); TipReferinta o2 = o1;nu face decˆt s˘ declare o nou˘ variabil˘ o2 ca referinta la obiectul referit de a a a a ¸o1 ¸i nu creeaz˘ sub nici o form˘ un nou obiect. s a a O posibilitate de a face o copie a unui obiect este folosirea metodei clonedefinit˘ ˆ clasa Object. Aceasta creeaz˘ un nou obiect ¸i initializeaz˘ toate a ın a s ¸ avariabilele sale membre cu valorile obiectului clonat. TipReferinta o1 = new TipReferinta(); TipReferinta o2 = (TipReferinta) o1.clone(); Deficienta acestei metode este c˘ nu realizeaz˘ duplicarea ˆ ¸ a a ıntregii retele ¸de obiecte corespunz˘toare obiectului clonat. In cazul ˆ care exist˘ cˆmpuri a ın a areferinta la alte obiecte, obiectele referite nu vor mai fi clonate la rˆndul lor. ¸ a O metod˘ clone care s˘ realizeze o copie efectiv˘ a unui obiect, ˆ a a a ımpreunacu copierea tuturor obiectelor referite de cˆmpurile acelui obiect poate fi aimplementat˘ prin mecanismul serializ˘rii astfel: a apublic 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) {
  • 198. 8.4. CLONAREA OBIECTELOR 197 System.out.println(e); return null; }}
  • 199. 198 CAPITOLUL 8. SERIALIZAREA OBIECTELOR
  • 200. Capitolul 9Interfata grafic˘ cu utilizatorul ¸ a9.1 IntroducereInterfata grafic˘ cu utilizatorul (GUI), este un termen cu ˆ ¸eles larg care ¸ a ıntse refer˘ la toate tipurile de comunicare vizual˘ ˆ a a ıntre un program ¸i utiliza- storii s˘i. Aceasta este o particularizare a interfetei cu utilizatorul (UI), prin a ¸care vom ˆ ıntelege conceptul generic de interactiune dintre program ¸i utiliza- ¸ stori. Limbajul Java pune la dispozitie numeroase clase pentru implementarea ¸diverselor functionalitati UI, ˆ a ne vom ocupa ˆ continuare de cele care ¸ ıns˘ ınpermit realizarea intefetei grafice cu utilizatorul (GUI). ¸ De la aparitia limbajului Java, bibliotecile de clase care ofer˘ servicii ¸ agrafice au suferit probabil cele mai mari schimb˘ri ˆ trecerea de la o ver- a ınsiune la alta. Acest lucru se datoreaz˘, pe de o parte dificult˘¸ii legate de a atimplementarea notiunii de portabilitate, pe de alt˘ parte nevoii de a integra ¸ amecanismele GUI cu tehnologii ap˘rute ¸i dezvoltate ulterior, cum ar fi Java a sBeans. In momentul actual, exist˘ dou˘ modalit˘¸i de a crea o aplicatie cu a a at ¸interfata grafic˘ ¸i anume: ¸˘ as • AWT (Abstract Windowing Toolkit) - este API-ul initial pus la dispozitie ¸ ¸ ˆ ıncepˆnd cu primele versiuni de Java; a • Swing - parte dintr-un proiect mai amplu numit JFC (Java Founda- tion Classes) creat ˆ urma colabor˘rii dintre Sun, Netscape ¸i IBM, ın a s Swing se bazeaz˘ pe modelul AWT, extinzˆnd functionalitatea acestuia a a ¸ ¸i ad˘ugˆnd sau ˆ s a a ınlocuind componente pentru dezvoltarea aplicatiilor ¸ GUI. 199
  • 201. 200 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ A¸adar, este de preferat ca aplicatiile Java s˘ fie create folosind tehnologia s ¸ aSwing, aceasta punˆnd la dispozitie o palet˘ mult mai larg˘ de facilit˘¸i, a ¸ a a atˆ a nu vom renunta complet la AWT deoarece aici exist˘ clase esentiale,ıns˘ ¸ a ¸reutilizate ˆ Swing. ın In acest capitol vom prezenta clasele de baz˘ ¸i mecanismul de tratare a asevenimentelor din AWT, deoarece va fi simplificat procesul de ˆ ¸elegere a ıntdezvolt˘rii unei aplicatii GUI, dup˘ care vom face trecerea la Swing. a ¸ a In principiu, crearea unei aplicatii grafice presupune urm˘toarele lucruri: ¸ a • Design – Crearea unei suprafete de afi¸are (cum ar fi o fereastr˘) pe care vor ¸ s a fi a¸ezate obiectele grafice (componente) care servesc la comuni- s carea cu utilizatorul (butoane, controale pentru editarea textelor, liste, etc); – Crearea ¸i a¸ezarea componentelor pe suprafata de afi¸are la pozitiile s s ¸ s ¸ corespunz˘toare; a • Functionalitate ¸ – Definirea unor actiuni care trebuie s˘ se execute ˆ momentul cˆnd ¸ a ın a utilizatorul interactioneaz˘ cu obiectele grafice ale aplicatiei; ¸ a ¸ – ”Ascultarea” evenimentelor generate de obiecte ˆ momentul ın interactiunii cu utilizatorul ¸i executarea actiunilor corespunz˘toare, ¸ s ¸ a a¸a cum au fost ele definite. s9.2 Modelul AWTPachetul care ofer˘ componente AWT este java.awt. aObiectele grafice sunt derivate din Component, cu exceptia meniurilor care ¸descind din clasa MenuComponent. A¸adar, prin notiunea de component˘ s ¸ avom ˆ ıntelege ˆ continuare orice obiect care poate avea o reprezentare grafic˘ ın a¸i care poate interactiona cu utilizatorul. Exemple de componente sunt fere-sstrele, butoanele, listele, bare de defilare, etc. Toate componentele AWT suntdefinte de clase proprii ce se gasesc ˆ pachetul java.awt, clasa Component ınfiind superclasa abstract˘ a tuturor acestor clase. a Crearea obiectelor grafice nu realizeaz˘ automat ¸i afi¸area lor pe ecran. a s sMai ˆ ai ele trebuie a¸ezate pe o suprafata de afi¸are, care poate fi o fereastr˘ ıntˆ s s a
  • 202. 9.2. MODELUL AWT 201sau un applet, ¸i vor deveni vizibile ˆ momentul ˆ care suprafata pe care sunt s ın ın ¸afi¸ate va fi vizibil˘. O astfel de suprafat˘ pe care sunt plasate componente s a ¸ase mai nume¸te container ¸i reprezint˘ o instant˘ a unei clase derivate din s s a ¸aContainer. Clasa Container este o subclas˘ aparte a lui Component, fiind ala rˆndul ei superclasa tuturor suprafetelor de afi¸are Java. a s A¸a cum am v˘zut, interfat˘ grafic˘ serve¸te interactiunii cu utilizatorul. s a ¸a a s ¸De cele mai multe ori programul trebuie s˘ fac˘ o anumit˘ prelucrare ˆ a a a ınmomentul ˆ care utilizatorul a efectuat o actiune ¸i, prin urmare, compo- ın ¸ snentele trebuie s˘ genereze evenimente ˆ functie de actiunea pe care au a ın ¸ ¸suferit-o (actiune transmis˘ de la tastatur˘, mouse, etc.). Incepˆnd cu ver- ¸ a a asiunea 1.1 a limbajului Java, evenimentele sunt instante ale claselor derivate ¸din AWTEvent.A¸adar, un eveniment este produs de o actiune a utilizatorului asupra unui s ¸obiect grafic, deci evenimentele nu trebuie generate de programator. Inschimb, ˆ ıntr-un program trebuie specificat codul care se execut˘ la aparitia a ¸unui eveniment. Tratarea evenimentelor se realizeaz˘ prin intermediul unor aclase de tip listener (ascult˘tor, consumator de evenimente), clase care sunt adefinite ˆ pachetul java.awt.event. In Java, orice component˘ poate ”con- ın asuma” evenimentele generate de o alt˘ component˘ (vezi ”Tratarea eveni- a amentelor”). S˘ consider˘m un mic exemplu, ˆ care cre˘m o fereastr˘ ce contine dou˘ a a ın a a ¸ abutoane. Listing 9.1: O fereastr˘ cu dou˘ butoane a aimport 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 ) ;
  • 203. 202 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ f . add ( b2 ) ; f . pack () ; // Afisarea fereastrei f . show () ; }} Dup˘ cum veti observa la executia acestui program, atˆt butoanele ad˘ugate a ¸ ¸ a ade noi cˆt ¸i butonul de ˆ a s ınchidere a ferestrei sunt functionale, adic˘ pot fi ¸ aapasate, dar nu realizeaz˘ nimic. Acest lucru se ˆ ampl˘ deoarece nu am a ıntˆ aspecificat nic˘ieri codul care trebuie s˘ se execute la ap˘sarea acestor bu- a a atoane.De asemenea, mai trebuie remarcat c˘ nu am specificat nic˘ieri dimensiu- a anile ferestrei sau ale butoanelor ¸i nici pozitiile ˆ acestea s˘ fie plasate. Cu s ın atoate acestea ele sunt plasate unul lˆnga celalalt, f˘r˘ s˘ se suprapun˘ iar a aa a asuprafata fereastrei este suficient de mare cˆt s˘ cuprind˘ ambele obiecte. ¸ a a aAceste ”fenomene” sunt provocate de un obiect special de tip FlowLayoutpe care l-am specificat ¸i care se ocup˘ cu gestionarea ferestrei ¸i cu plasarea s a scomponentelor ˆ ıntr-o anumit˘ ordine pe suprafata ei. A¸adar, modul de a ¸ saranjare nu este o caracteristic˘ a suprafetei de afi¸are ci, fiecare container a ¸ sare asociat un obiect care se ocup˘ cu dimensionarea ¸i dispunerea compo- a snentelor pe suprafata de afi¸are ¸i care se numeste gestionar de pozitionare ¸ s s ¸(layout manager) (vezi ”Gestionarea pozition˘rii”). ¸ a9.2.1 Componentele AWTDup˘ cum am spus deja, toate componentele AWT sunt definte de clase aproprii ce se gasesc ˆ pachetul java.awt, clasa Component fiind superclasa ınabstracta a tuturor acestor clase. • Button - butoane cu eticheta format˘ dintr-un text pe o singur˘ linie; a a • Canvas - suprafat˘ pentru desenare; ¸a • Checkbox - component˘ ce poate avea dou˘ st˘ri; mai multe obiecte a a a de acest tip pot fi grupate folosind clasa CheckBoxGroup; • Choice - liste ˆ care doar elementul selectat este vizibil ¸i care se ın s deschid la ap˘sarea lor; a
  • 204. 9.2. MODELUL AWT 203 • Container - superclasa tuturor suprafetelor de afi¸are (vezi ”Suprafete ¸ s ¸ de afi¸are”); s • Label - etichete simple ce pot contine o singur˘ linie de text needitabil; ¸ a • List - liste cu selectie simpl˘ sau multipl˘; ¸ a a • Scrollbar - bare de defilare orizontale sau verticale; • TextComponent - superclasa componentelor pentru editarea textu- lui: TextField (pe o singur˘ linie) ¸i TextArea (pe mai multe linii). a s Mai multe informatii legate de aceste clase vor fi prezentate ˆ sectiunea ¸ ın ¸”Folosirea componentelor AWT”. Din cauza unor diferente esentiale ˆ implementarea meniurilor pe diferite ¸ ınplatforme de operare, acestea nu au putut fi integrate ca obiecte de tipComponent, superclasa care descrie meniuri fiind MenuComponent (vezi”Meniuri”). Componentele AWT au peste 100 de metode comune, mo¸tenite din clasa sComponent. Acestea servesc uzual pentru aflarea sau setarea atributelorobiectelor, cum ar fi: dimensiune, pozitie, culoare, font, etc. ¸i au formatul ¸ sgeneral getProprietate, respectiv setProprietate. Cele mai folosite, grupatepe tipul propriet˘¸ii gestionate sunt: at • Pozitie ¸ getLocation, getX, getY, getLocationOnScreen setLocation, setX, setY • Dimensiuni getSize, getHeight, getWidth setSize, setHeight, setWidth • Dimensiuni ¸i pozitie s ¸ getBounds setBounds • Culoare (text ¸i fundal) s getForeground, getBackground setForeground, setBackground
  • 205. 204 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ • Font getFont setFont • Vizibilitate setVisible isVisible • Interactivitate setEnabled isEnabled9.2.2 Suprafete de afi¸are (Clasa Container) ¸ sCrearea obiectelor grafice nu realizeaz˘ automat ¸i afi¸area lor pe ecran. Mai a s sˆ ai ele trebuie a¸ezate pe o suprafat˘, care poate fi o fereastr˘ sau suprafataıntˆ s ¸a a ¸unui applet, ¸i vor deveni vizibile ˆ momentul ˆ care suprafata respectiv˘ s ın ın ¸ ava fi vizibil˘. O astfel de suprafata pe care sunt plasate componentele se a ¸nume¸te suprafat˘ de afi¸are sau container ¸i reprezint˘ o instanta a unei clase s ¸a s s a ¸derivat˘ din Container. O parte din clasele a c˘ror p˘rinte este Container a a aeste prezentat˘ mai jos: a • Window - este superclasa tututor ferestrelor. Din aceast˘ clas˘ sunt a a derivate: – Frame - ferestre standard; – Dialog - ferestre de dialog modale sau nemodale; • Panel - o suprafat˘ f˘r˘ reprezentare grafic˘ folosit˘ pentru gruparea ¸a a a a a altor componente. Din aceast˘ clas˘ deriv˘ Applet, folosit˘ pentru a a a a crearea appleturilor. • ScrollPane - container folosit pentru implementarea automat˘ a derul˘rii a a pe orizontal˘ sau vertical˘ a unei componente. a a A¸adar, un container este folosit pentru a ad˘uga componente pe suprafata s a ¸lui. Componentele ad˘ugate sunt memorate ˆ a ıntr-o list˘ iar pozitiile lor din a ¸aceast˘ list˘ vor defini ordinea de traversare ”front-to-back” a acestora ˆ a a ıncadrul containerului. Dac˘ nu este specificat nici un index la ad˘ugarea unei a acomponente, atunci ea va fi adaugat˘ pe ultima pozitie a listei. a ¸
  • 206. 9.2. MODELUL AWT 205 Clasa Container contine metodele comune tututor suprafetelor de afi¸are. ¸ ¸ sDintre cele mai folosite, amintim: • add - permite ad˘ugarea unei componente pe suprafata de afi¸are. a ¸ s O component˘ nu poate apartine decˆt unui singur container, ceea ce a ¸ a ˆ ınseamn˘ c˘ pentru a muta un obiect dintr-un container ˆ altul trebuie a a ın sa-l eliminam mai ˆ ai de pe containerul initial. ıntˆ • remove - elimin˘ o component˘ de pe container; a a • setLayout - stabile¸te gestionarul de pozitionare al containerului (vezi s ¸ ”Gestionarea pozition˘rii”); ¸ a • getInsets - determin˘ distanta rezervat˘ pentru marginile suprafetei a ¸ a ¸ de afi¸are; s • validate - forteaz˘ containerul s˘ rea¸eze toate componentele sale. ¸ a a s Aceast˘ metod˘ trebuie apelat˘ explicit atunci cˆnd ad˘ug˘m sau elimin˘m a a a a a a a componente pe suprafata de afi¸are dup˘ ce aceasta a devenit vizibil˘. ¸ s a a Exemplu: Frame f = new Frame("O fereastra"); // Adaugam un buton direct pe fereastra Button b = new Button("Hello"); f.add(b); // Adaugam doua componente pe un panel Label et = new Label("Nume:"); TextField text = new TextField(); Panel panel = new Panel(); panel.add(et); panel.add(text); // Adaugam panel-ul pe fereastra // si, indirect, cele doua componente f.add(panel);
  • 207. 206 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸9.3 Gestionarea pozition˘rii ¸ aS˘ consider˘m mai ˆ ai un exemplu de program Java care afi¸eaz˘ 5 butoane a a ıntˆ s ape o fereastr˘: a Listing 9.2: Pozitionarea a 5 butoane ¸import java . awt .*;public class TestLayout { public static void main ( String args []) { Frame f = new Frame ( " Grid Layout " ) ; f . setLayout ( new GridLayout (3 , 2) ) ; // * Button b1 = new Button ( " Button 1 " ) ; Button b2 = new Button ( " 2 " ) ; Button b3 = new Button ( " Button 3 " ) ; 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 () ; }} Fereastra afi¸ata de acest program va ar˘ta astfel: s a S˘ modific˘m acum linia marcata cu ’*’ ca mai jos, l˘sˆnd neschimbat a a aarestul programului: Frame f = new Frame("Flow Layout"); f.setLayout(new FlowLayout()); Fereastra afi¸at˘ dup˘ aceast˘ modificare va avea o cu totul altfel de s a a adispunere a componentelor sale:
  • 208. ¸ ˘9.3. GESTIONAREA POZITIONARII 207 Motivul pentru care cele dou˘ ferestre arat˘ atˆt de diferit este c˘ folosesc a a a agestionari de pozitionare diferiti: GridLayout, respectiv FlowLayout. ¸ ¸ Definitie ¸ Un gestionar de pozitionare (layout manager) este un obiect care con- ¸troleaz˘ dimensiunea ¸i aranjarea (pozitia) componentelor unui container. a s ¸ A¸adar, modul de aranjare a componentelor pe o suprafata de afi¸are s ¸ snu este o caracteristic˘ a containerului. Fiecare obiect de tip Container a(Applet, Frame, Panel, etc.) are asociat un obiect care se ocup˘ cu dis- apunerea componentelor pe suprafata sa ¸i anume gestionarul s˘u de pozitionare. ¸ s a ¸Toate clasele care instantiaza obiecte pentru gestionarea pozition˘rii imple- ¸ ¸ amenteaz˘ interfat˘ LayoutManager. a ¸a La instantierea unui container se creeaz˘ implicit un gestionar de pozitionare ¸ a ¸asociat acestuia. De exemplu, pentru o fereastr˘ gestionarul implict este de atip BorderLayout, ˆ timp ce pentru un panel este de tip FlowLayout. ın9.3.1 Folosirea gestionarilor de pozitionare ¸A¸a cum am v˘zut, orice container are un gestionar implicit de pozitionare - s a ¸un obiect care implemeneaz˘ interfata LayoutManager, acesta fiindu-i ata¸at a ¸ sautomat la crearea sa. In cazul ˆ care acesta nu corespunde necesit˘¸ilor ın atnoastre, el poate fi schimbat cu u¸urint˘. Cei mai utilizati gestionari din s ¸a ¸pachetul java.awt sunt: • FlowLayout • BorderLayout • GridLayout • CardLayout • GridBagLayout
  • 209. 208 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Pe lˆng˘ ace¸tia, mai exist˘ ¸i cei din modelul Swing care vor fi prezentati a a s as ¸ˆ capitolul dedicat dezvolt˘rii de aplicatii GUI folosind Swing.ın a ¸ Ata¸area explicit˘ a unui gestionar de pozitionare la un container se face s a ¸cu metoda setLayout a clasei Container. Metoda poate primi ca parametruorice instant˘ a unei clase care implementeaz˘ interfat˘ LayoutManager. ¸a a ¸aSecventa de ata¸are a unui gestionar pentru un container, particularizat˘ ¸ s apentru FlowLayout, este: FlowLayout gestionar = new FlowLayout(); container.setLayout(gestionar); // sau, mai uzual: container.setLayout(new FlowLayout()); Programele nu apeleaz˘ ˆ general metode ale gestionarilor de pozitionare, a ın ¸dar ˆ cazul cˆnd avem nevoie de obiectul gestionar ˆ putem obtine cu metoda ın a ıl ¸getLayout din clasa Container. Una din facilit˘¸ile cele mai utile oferite de gestionarii de pozitionare at ¸este rearanjarea componentele unui container atunci cˆnd acesta este red- aimesionat. Pozitiile ¸i dimensiunile componentelor nu sunt fixe, ele fiind ¸ sajustate automat de c˘tre gestionar la fiecare redimensionare astfel ˆ at s˘ a ıncˆ aocupe cˆt mai ”estetic” suprafata de afi¸are. Cum sunt determinate ˆ a a ¸ s ıns˘dimensiunile implicite ale componentelor ?Fiecare clas˘ derivat˘ din Component poate implementa metodele getPre- a aferredSize, getMinimumSize ¸i getMaximumSize care s˘ returneze di- s amensiunea implicit˘ a componentei respective ¸i limitele ˆ afara c˘rora com- a s ın aponenta nu mai poate fi desenat˘. Gestionarii de pozitionare vor apela aceste a ¸metode pentru a calcula dimensiunea la care vor afi¸a o component˘. s a Sunt ˆ a situatii cˆnd dorim s˘ plas˘m componentele la anumite pozitii ıns˘ ¸ a a a ¸fixe iar acestea s˘ ramˆna acolo chiar dac˘ redimension˘m containerul. Folosind a a a aun gestionar aceast˘ pozitionare absolut˘ a componentelor nu este posibil˘ ¸i a ¸ a asdeci trebuie cumva s˘ renunt˘m la gestionarea automat˘ a containerul. Acest a ¸a alucru se realizeaz˘ prin trimitera argumentului null metodei setLayout: a // pozitionare absoluta a componentelor in container container.setLayout(null); Folosind pozitionarea absolut˘, nu va mai fi ˆ a suficient s˘ ad˘ugam cu ¸ a ıns˘ a ametoda add componentele ˆ container, ci va trebui s˘ specific˘m pozitia ¸i ın a a ¸ s
  • 210. ¸ ˘9.3. GESTIONAREA POZITIONARII 209dimensiunea lor - acest lucru era f˘cut automat de gestionarul de pozitionare. a ¸ container.setLayout(null); Button b = new Button("Buton"); b.setSize(10, 10); b.setLocation (0, 0); container.add(b); In general, se recomand˘ folosirea gestionarilor de pozitionare ˆ toate a ¸ ınsituatiile cˆnd acest lucru este posibil, deoarece permit programului s˘ aib˘ ¸ a a aaceea¸i ”ˆ s ınfatisare” indiferent de platforma ¸i rezolutia pe care este rulat. s ¸Pozitionarea absolut˘ poate ridica diverse probleme ˆ acest sens. ¸ a ın S˘ analizam ˆ continuare pe fiecare din gestionarii amintiti anterior. a ın ¸9.3.2 Gestionarul FlowLayoutAcest gestionar a¸eaz˘ componentele pe suprafata de afi¸are ˆ flux liniar, mai s a ¸ s ınprecis, componentele sunt ad˘ugate una dup˘ alta pe linii, ˆ limita spatiului a a ın ¸disponibil. In momentul cˆnd o component˘ nu mai ˆ a a ıncape pe linia curent˘ se atrece la urm˘toarea linie, de sus ˆ jos. Ad˘ugarea componentelor se face de a ın ala stˆnga la dreapta pe linie, iar alinierea obiectelor ˆ cadrul unei linii poate a ınfi de trei feluri: la stˆnga, la dreapta ¸i pe centru. Implicit, componentele a ssunt centrate pe fiecare linie iar distanta implicit˘ ˆ ¸ a ıntre componente este de5 pixeli pe vertical˘ ¸i 5 pe orizontal˘. as a Este gestionarul implicit al containerelor derivate din clasa Panel deci ¸i sal applet-urilor. Listing 9.3: Gestionarul FlowLayoutimport 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 " ) ;
  • 211. 210 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Button b4 = new Button ( " Long - Named Button 4 " ) ; Button b5 = new Button ( " Button 5 " ) ; f . add ( b1 ) ; f . add ( b2 ) ; f . add ( b3 ) ; f . add ( b4 ) ; f . add ( b5 ) ; f . pack () ; f . show () ; }} Componentele ferestrei vor fi afi¸ate astfel: s Redimensionˆnd fereastra astfel ˆ at cele cinci butoane s˘ nu mai ˆ a ıncˆ a ıncap˘ ape o linie, ultimele dintre ele vor fi trecute pe linia urm˘toare: a9.3.3 Gestionarul BorderLayoutGestionarul BorderLayout ˆ ımparte suprafata de afi¸are ˆ cinci regiuni, core- ¸ s ınspunz˘toare celor patru puncte cardinale ¸i centrului. O component˘ poate fi a s aplasat˘ ˆ oricare din aceste regiuni, dimeniunea componentei fiind calculata a ınastfel ˆ at s˘ ocupe ˆ ıncˆ a ıntreg spatiul de afi¸are oferit de regiunea respectiv˘. ¸ s aPentru a ad˘uga mai multe obiecte grafice ˆ a ıntr-una din cele cinci zone, eletrebuie grupate ˆ prealabil ˆ ın ıntr-un panel, care va fi amplasat apoi ˆ regiunea ındorit˘ (vezi ”Gruparea componentelor - clasa Panel”). a A¸adar, la ad˘ugarea unei componente pe o suprafat˘ gestionat˘ de BorderLayout, s a ¸a ametoda add va mai primi pe lˆnga referinta componentei ¸i zona ˆ care a ¸ s ınaceasta va fi amplasat˘, care va fi specificat˘ prin una din constantele clasei: a aNORTH, SOUTH, EAST, WEST, CENTER. BorderLayout este gestionarul implicit pentru toate containerele care de-scind din clasa Window, deci al tuturor tipurilor de ferestre.
  • 212. ¸ ˘9.3. GESTIONAREA POZITIONARII 211 Listing 9.4: Gestionarul BorderLayoutimport 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 Button ( " Nord " ) , BorderLayout . NORTH ) ; f . add ( new Button ( " Sud " ) , BorderLayout . SOUTH ) ; f . add ( new Button ( " Est " ) , BorderLayout . EAST ) ; f . add ( new Button ( " Vest " ) , BorderLayout . WEST ) ; f . add ( new Button ( " Centru " ) , BorderLayout . CENTER ) ; f . pack () ; f . show () ; }} Cele cinci butoane ale ferestrei vor fi afi¸ate astfel: s La redimensionarea ferestrei se pot observa urm˘toarele lucruri: nordul ¸i a ssudul se redimensioneaz˘ doar pe orizontal˘, estul ¸i vestul doar pe vertical˘, a a s aˆ timp ce centrul se redimensioneaz˘ atˆt pe orizontal˘ cˆt ¸i pe vertical˘.ın a a a a s aRedimensionarea componentelor din fiecare zon˘ se face astfel ˆ at ele ocup˘ a ıncˆ atoat˘ zona containerului din care fac parte. a9.3.4 Gestionarul GridLayoutGestionarul GridLayout organizeaz˘ containerul ca un tabel cu rˆnduri ¸i a a scoloane, componentele fiind plasate ˆ celulele tabelului de la stˆnga la ın adreapta, ˆ ıncepˆnd cu primul rˆnd. Celulele tabelului au dimensiuni egale a aiar o component˘ poate ocupa doar o singur˘ celul˘. Num˘rul de linii ¸i a a a a scoloane vor fi specificate ˆ constructorul gestionarului, dar pot fi modificate ın
  • 213. 212 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸¸i ulterior prin metodele setRows, respectiv setCols. Dac˘ num˘rul de liniis a asau coloane este 0 (dar nu ambele ˆ acela¸i timp), atunci componentele vor ın sfi plasate ˆıntr-o singur˘ coloan˘ sau linie. De asemenea, distanta ˆ a a ¸ ıntre com-ponente pe orizontal˘ ¸i distanta ˆ as ıntre rˆndurile tabelului pot fi specificate aˆ constructor sau stabilite ulterior.ın Listing 9.5: Gestionarul GridLayoutimport 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 Button ( " 1 " ) ) ; f . add ( new Button ( " 2 " ) ) ; f . add ( new Button ( " 3 " ) ) ; f . add ( new Button ( " 4 " ) ) ; f . add ( new Button ( " 5 " ) ) ; f . add ( new Button ( " 6 " ) ) ; f . pack () ; f . show () ; }} Cele ¸ase butoane ale ferestrei vor fi plasate pe trei rˆnduri ¸i dou˘ s a s acoloane, astfel: Redimensionarea ferestrei va determina redimensionarea tuturor celulelor¸i deci a tuturor componentelor, atˆt pe orizontal˘ cˆt ¸i pe vertical˘.s a a a s a9.3.5 Gestionarul CardLayoutGestionarul CardLayout trateaz˘ componentele ad˘ugate pe suprafata sa a a ¸ˆıntr-o manier˘ similar˘ cu cea a dispunerii c˘rtilor de joc ˆ a a a¸ ıntr-un pachet.
  • 214. ¸ ˘9.3. GESTIONAREA POZITIONARII 213Suprafata de afi¸are poate fi asem˘nat˘ cu pachetul de c˘rti iar fiecare com- ¸ s a a a¸ponent˘ este o carte din pachet. La un moment dat, numai o singur˘ com- a aponent˘ este vizibil˘ (”cea de deasupra”). a a Clasa dispune de metode prin care s˘ poat˘ fi afi¸at˘ o anumit˘ com- a a s a aponent˘ din pachet, sau s˘ se poat˘ parcurge secvential pachetul, ordinea a a a ¸componentelor fiind intern˘ gestionarului. a Principala utilitate a acestui gestionar este utilizarea mai eficient˘ a aspatiului disponibil ˆ situatii ˆ care componentele pot fi grupate ˆ a¸a ¸ ın ¸ ın ın sfel ˆ at utilizatorul s˘ interactioneze la un moment dat doar cu un anumit ıncˆ a ¸grup (o carte din pachet), celelalte fiind ascunse.O clas˘ Swing care implementeaz˘ un mecansim similar este JTabbedPane. a a Listing 9.6: Gestionarul CardLayoutimport 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 () ;
  • 215. 214 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ card1 . addActionListe ner ( this ) ; card2 . addActionListe ner ( this ) ; } public void actionPerformed ( ActionEvent e ) { CardLayout gestionar = ( CardLayout ) tab . getLayout () ; gestionar . show ( tab , e . getActionCommand () ) ; } public static void main ( String args []) { TestCardLayout f = new TestCardLayout () ; f . show () ; }} Prima ”carte” este vizibil˘ a A doua ”carte” este vizibil˘ a9.3.6 Gestionarul GridBagLayoutEste cel mai complex ¸i flexibil gestionar de pozitionare din Java. La fel ca ˆ s ¸ ıncazul gestionarului GridLayout, suprafata de afi¸are este considerat˘ ca fiind ¸ s aun tabel ˆ a, spre deosebire de acesta, num˘rul de linii ¸i de coloane sunt ıns˘ a sdeterminate automat, ˆ functie de componentele amplasate pe suprafata de ın ¸ ¸afi¸are. De asemenea, ˆ functie de componentele gestionate, dimensiunile s ın ¸celulelor pot fi diferite cu singurele restrictii ca pe aceea¸i linie s˘ aib˘ aceea¸i ¸ s a a sˆ ¸ime, iar pe coloan˘ aib˘ aceea¸i l˘¸ime. Spre deosebire de GridLayout,ınalt a a s ato component˘ poate ocupa mai multe celule adiacente, chiar de dimensiuni adiferite, zona ocupat˘ fiind referit˘ prin ”regiunea de afi¸are” a componentei a a srespective. Pentru a specifica modul de afi¸are a unei componente, acesteia ˆ este s ıiasociat un obiect de tip GridBagConstraints, ˆ care se specific˘ diferite ın apropriet˘¸i ale componentei referitoare la regiunea s˘ de afi¸are ¸i la modul at a s sˆ care va fi plasat˘ ˆ aceast˘ regiune. Leg˘tura dintre o component˘ ¸i unın a ın a a asobiect GridBagConstraints se realizeaz˘ prin metoda setConstraints: a GridBagLayout gridBag = new GridBagLayout();
  • 216. ¸ ˘9.3. GESTIONAREA POZITIONARII 215 container.setLayout(gridBag); GridBagConstraints c = new GridBagConstraints(); //Specificam restrictiile referitoare la afisarea componentei . . . gridBag.setConstraints(componenta, c); container.add(componenta); A¸adar, ˆ s ınainte de a ad˘uga o component˘ pe suprafata unui container a a ¸care are un gestionar de tip GridBagLayout, va trebui s˘ specific˘m anumiti a a ¸parametri (constrˆngeri) referitori la cum va fi plasat˘ componenta respec- a ativ˘. Aceste constrˆngeri vor fi specificate prin intermediul unui obiect de tip a aGridBagConstraints, care poate fi refolosit pentru mai multe componentecare au acelea¸i constrˆngeri de afi¸are: s a s gridBag.setConstraints(componenta1, c); gridBag.setConstraints(componenta2, c); . . . Cele mai utilizate tipuri de constrˆngeri pot fi specificate prin intermediul aurm˘toarelor variabile din clasa GridBagConstraints: a • gridx, gridy - celula ce reprezint˘ coltul stˆnga sus al componentei; a ¸ a • gridwidth, gridheight - num˘rul de celule pe linie ¸i coloan˘ pe care a s a va fi afi¸at˘ componenta; s a • fill - folosit˘ pentru a specifica dac˘ o component˘ va ocupa ˆ a a a ıntreg spatiul pe care ˆ are destinat; valorile posibile sunt HORIZONTAL, VERTICAL, ¸ ıl BOTH, NONE; • insets - distantele dintre component˘ ¸i marginile suprafetei sale de ¸ as ¸ afi¸are; s • anchor - folosit˘ atunci cˆnd componenta este mai mic˘ decˆt suprafata a a a a ¸ sa de afi¸are pentru a forta o anumit˘ dispunere a sa: nord, sud, est, s ¸ a vest, etc. • weigthx, weighty - folosite pentru distributia spatiului liber; uzual ¸ ¸ au valoarea 1;
  • 217. 216 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Ca exemplu, s˘ realiz˘m o fereastr˘ ca ˆ figura de mai jos. Pentru a a a a ınsimplifica codul, a fost creat˘ o metod˘ responsabil˘ cu setarea valorilor a a agridx, gridy, gridwidth, gridheight ¸i ad˘ugarea unei componente cu s arestrictiile stabilite pe fereastr˘. ¸ a Listing 9.7: Gestionarul GridBagLayoutimport java . awt .*;public class TestGridBag Layout { static Frame f ; static GridBagLayout gridBag ; static GridBagConstr aint s gbc ; static void adauga ( Component comp , int x , int y , int w , int h ) { gbc . gridx = x ; gbc . gridy = y ; gbc . gridwidth = w ; gbc . gridheight = h ; gridBag . setConstraints ( comp , gbc ) ; f . add ( comp ) ; } public static void main ( String args []) { f = new Frame ( " Test GridBagLayout " ) ; gridBag = new GridBagLayout () ; gbc = new GridBa gC ons tr ai nt s () ; gbc . weightx = 1.0; gbc . weighty = 1.0;
  • 218. ¸ ˘9.3. GESTIONAREA POZITIONARII 217 gbc . insets = new Insets (5 , 5 , 5 , 5) ; f . setLayout ( gridBag ) ; Label mesaj = new Label ( " Evidenta persoane " , Label . CENTER ); mesaj . setFont ( new Font ( " Arial " , Font . BOLD , 24) ) ; mesaj . setBackground ( Color . yellow ) ; gbc . fill = GridBagConst ra in ts . BOTH ; adauga ( mesaj , 0 , 0 , 4 , 2) ; Label etNume = new Label ( " Nume : " ) ; gbc . fill = GridBagConst ra in ts . NONE ; gbc . anchor = GridBagCon st ra in ts . EAST ; adauga ( etNume , 0 , 2 , 1 , 1) ; Label etSalariu = new Label ( " Salariu : " ) ; adauga ( etSalariu , 0 , 3 , 1 , 1) ; TextField nume = new TextField ( " " , 30) ; gbc . fill = GridBagConst ra in ts . HORIZONTAL ; gbc . anchor = GridBagCon st ra in ts . CENTER ; adauga ( nume , 1 , 2 , 2 , 1) ; TextField salariu = new TextField ( " " , 30) ; adauga ( salariu , 1 , 3 , 2 , 1) ; Button adaugare = new Button ( " Adaugare " ) ; gbc . fill = GridBagConst ra in ts . NONE ; adauga ( adaugare , 3 , 2 , 1 , 2) ; Button salvare = new Button ( " Salvare " ) ; gbc . fill = GridBagConst ra in ts . HORIZONTAL ; adauga ( salvare , 1 , 4 , 1 , 1) ; Button iesire = new Button ( " Iesire " ) ; adauga ( iesire , 2 , 4 , 1 , 1) ; f . pack () ; f . show () ; }}
  • 219. 218 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸9.3.7 Gruparea componentelor (Clasa Panel)Plasarea componentelor direct pe suprafata de afi¸are poate deveni incomod˘ ¸ s aˆ cazul ˆ care avem multe obiecte grafice. Din acest motiv, se recomand˘ın ın agruparea componentelor ˆ ınrudite ca functii astfel ˆ at s˘ putem fi siguri c˘, ¸ ıncˆ a aindiferent de gestionarul de pozitionare al suprafetei de afi¸are, ele se vor g˘si ¸ ¸ s aˆımpreun˘. Gruparea componentelor se face ˆ panel-uri. a ın Un panel este cel mai simplu model de container. El nu are o reprezentarevizibil˘, rolul s˘u fiind de a oferi o suprafat˘ de afi¸are pentru componente a a ¸a sgrafice, inclusiv pentru alte panel-uri. Clasa care instantiaza aceste obiecte ¸este Panel, extensie a superclasei Container. Pentru a aranja corespunz˘tor acomponentele grupate ˆ ıntr-un panel, acestuia i se poate specifica un gestionarde pozitionare anume, folosind metoda setLayout. Gestionarul implicit pen- ¸tru containerele de tip Panel este FlowLayout. A¸adar, o aranjare eficient˘ a componentelor unei ferestre ˆ s a ınseamn˘:a • gruparea componentelor ”ˆ ınfratite” (care nu trebuie s˘ fie despartite ¸ a ¸ de gestionarul de pozitionare al ferestrei) ˆ panel-uri; ¸ ın • aranjarea componentelor unui panel, prin specificarea unui gestionar de pozitionare corespunz˘tor; ¸ a • aranjarea panel-urilor pe suprafata ferestrei, prin specificarea gestionaru- ¸ lui de pozitionare al ferestrei. ¸ Listing 9.8: Gruparea componentelorimport 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 " ) ) ;
  • 220. 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 evenimentelorUn eveniment este produs de o actiune a utilizatorului asupra unei compo- ¸nente grafice ¸i reprezint˘ mecanismul prin care utilizatorul comunic˘ efectiv s a acu programul. Exemple de evenimente sunt: ap˘sarea unui buton, modi- aficarea textului ˆ ıntr-un control de editare, ˆınchiderea sau redimensionareaunei ferestre, etc. Componentele care genereaz˘ anumite evenimente se mai anumesc ¸i surse de evenimente. s Interceptarea evenimentelor generate de componentele unui program serealizeaz˘ prin intermediul unor clase de tip listener (ascult˘tor, consumator a ade evenimente). In Java, orice obiect poate ”consuma” evenimentele generatede o anumit˘ component˘ grafic˘. a a a A¸adar, pentru a scrie cod care s˘ se execute ˆ momentul ˆ care utiliza- s a ın ıntorul interactioneaz˘ cu o component˘ grafic˘ trebuie s˘ facem urm˘toarele a a a a alucruri: • s˘ scriem o clas˘ de tip listener care s˘ ”asculte” evenimentele produse a a a de acea component˘ ¸i ˆ cadrul acestei clase s˘ implement˘m metode a s ın a a specifice pentru tratarea lor;
  • 221. 220 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ • s˘ comunic˘m componentei surs˘ c˘ respectiva clasa ˆ ”ascult˘” eveni- a a a a ıi a mentele pe care le genereaz˘, cu alte cuvinte s˘ ˆ a a ınregistr˘m acea clas˘ a a drept ”consumator” al evenimentelor produse de componenta respec- tiv˘. a Evenimentele sunt, ca orice altceva ˆ Java, obiecte. Clasele care descriu ınaceste obiecte se ˆımpart ˆ mai multe tipuri ˆ functie de componenta care ın ın ¸le genereaz˘, mai precis ˆ functie de actiunea utilizatorului asupra acesteia. a ın ¸ ¸Pentru fiecare tip de eveniment exist˘ o clas˘ care instantiaz˘ obiecte de a a ¸ aacel tip. De exemplu, evenimentul generat de actionarea unui buton este ¸descris de clasa ActionEvent, cel generat de modificarea unui text de clasaTextEvent, etc. Toate aceste clase sunt derivate din superclasa AWTEvent,lista lor complet˘ fiind prezentat˘ ulterior. a a O clas˘ consumatoare de evenimente (listener) poate fi orice clas˘ care a aspecifica ˆ declaratia sa c˘ dore¸te s˘ asculte evenimente de un anumit ın ¸ a s atip. Acest lucru se realizeaz˘ prin implementarea unei interfete specifice a ¸fiec˘rui tip de eveniment. Astfel, pentru ascultarea evenimentelor de tip aActionEvent clasa respectiv˘ trebuie s˘ implementeze interfata ActionListener, a a ¸pentru TextEvent interfat˘ care trebuie implementata este TextListener, ¸aetc. Toate aceste interfete sunt derivate din EventListener. ¸ Fiecare interfat˘ define¸te una sau mai multe metode care vor fi apelate ¸a sautomat la aparitia unui eveniment: ¸class AscultaButoane implements ActionListener { public void actionPerformed(ActionEvent e) { // Metoda interfetei ActionListener ... }}class AscultaTexte implements TextListener { public void textValueChanged(TextEvent e) { // Metoda interfetei TextListener ... }} Intrucˆt o clas˘ poate implementa oricˆte interfete, ea va putea s˘ asculte a a a ¸ aevenimente de mai multe tipuri:
  • 222. 9.4. TRATAREA EVENIMENTELOR 221class Ascultator implements ActionListener, TextListener { public void actionPerformed(ActionEvent e) { ... } public void textValueChanged(TextEvent e) { ... }} Vom vedea ˆ continuare metodele fiec˘rei interfete pentru a ¸ti ce trebuie ın a ¸ ss˘ implementeze o clas˘ consumatoare de evenimente. a a A¸a cum am spus mai devreme, pentru ca evenimentele unei componente ss˘ fie interceptate de c˘tre o instant˘ a unei clase ascult˘tor, aceast˘ clas˘ a a ¸a a a atrebuie ˆınregistrata ˆ lista ascult˘torilor componentei respective. Am spus ın alista, deoarece evenimentele unei componente pot fi ascultate de oricˆte clase, acu conditia ca acestea s˘ fie ˆ ¸ a ınregistrate la componenta respectiv˘. Inregis- atrarea unei clase ˆ lista ascult˘torilor unei componente se face cu metode ın adin clasa Component de tipul addTipEvenimentListener, iar eliminarea eidin aceast˘ list˘ cu removeTipEvenimentListener. a a Sumarizˆnd, tratarea evenimentelor ˆ Java se desf˘¸oar˘ astfel: a ın as a • Componentele genereaz˘ evenimente cˆnd ceva ”interesant” se ˆ ampl˘; a a ıntˆ a • Sursele evenimentelor permit oric˘rei clase s˘ ”asculte” evenimentele a a sale prin metode de tip addXXXListener, unde XXX este un tip de eveniment; • O clas˘ care ascult˘ evenimente trebuie s˘ implementeze interfete speci- a a a ¸ fice fiec˘rui tip de eveniment - acestea descriu metode ce vor fi apelate a automat la aparitia evenimentelor. ¸9.4.1 Exemplu de tratare a evenimentelorInainte de a detalia aspectele prezentate mai sus, s˘ consider˘m un exemplu a ade tratare a evenimentelor. Vom crea o fereastr˘ care s˘ contin˘ dou˘ bu- a a ¸ a atoane cu numele ”OK”, repectiv ”Cancel”. La ap˘sarea fiec˘rui buton vom a ascrie pe bara de titlu a ferestrei mesajul ”Ati apasat butonul ...”. Listing 9.9: Ascultarea evenimentelor a dou˘ butoane aimport java . awt .*;import java . awt . event .*;
  • 223. 222 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setLayout ( new FlowLayout () ) ; setSize (200 , 100) ; Button b1 = new Button ( " OK " ) ; Button b2 = new Button ( " Cancel " ) ; add ( b1 ) ; add ( b2 ) ; Ascultator listener = new Ascultator ( this ) ; b1 . addActionListener ( listener ) ; b2 . addActionListener ( listener ) ; // Ambele butoane sunt ascultate de obiectul listener , // instanta a clasei Ascultator , definita mai jos }}class Ascultator implements ActionListener { private Fereastra f ; public Ascultator ( Fereastra f ) { this . f = f ; } // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { f . setTitle ( " Ati apasat " + e . getActionCommand () ) ; }}public class TestEvent1 { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test Event " ) ; f . show () ; }} Nu este obligatoriu s˘ definim clase speciale pentru ascultarea eveni- amentelor. In exemplul de mai sus am definit clasa Ascultator pentru aintercepta evenimentele produse de cele dou˘ butoane ¸i din acest motiv a a strebuit s˘ trimitem ca parametru constructorului clasei o referinta la fereas- a ¸tra noastr˘. Mai simplu ar fi fost s˘ folosim chiar clasa Fereastra pentru a aa trata evenimentele produse de componentele sale. Vom modifica putin ¸i ¸ s
  • 224. 9.4. TRATAREA EVENIMENTELOR 223aplicatia pentru a pune ˆ evidenta o alt˘ modalitate de a determina com- ¸ ın ¸ aponenta generatoare a unui eveniment - metoda getSource. Listing 9.10: Tratarea evenimentelor ˆ ferestr˘ ın aimport 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 () ; }}
  • 225. 224 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ A¸adar, orice clas˘ poate asculta evenimente de orice tip cu conditia s˘ s a ¸ aimplementeze interfetele specifice acelor tipuri de evenimente. ¸9.4.2 Tipuri de evenimenteEvenimentele se ˆ ımpart ˆ dou˘ categorii: de nivel jos ¸i semantice. ın a s Evenimentele de nivel jos reprezint˘ o interactiune de nivel jos cum ar a ¸fi o ap˘sare de tast˘, mi¸carea mouse-ului, sau o operatie asupra unei ferestre. a a s ¸In tabelul de mai jos sunt enumerate clasele ce descriu aceste evenimente ¸i soperatiunile efectuate (asupra unei componente) care le genereaz˘: ¸ a ComponentEvent Ascundere, deplasare, redimensionare, afi¸are s ContainerEvent Ad˘ugare pe container, eliminare a FocusEvent Obtinere, pierdere foucs ¸ KeyEvent Ap˘sare, eliberare taste, tastare a MouseEvent Operatiuni cu mouse-ul: click, drag, etc. ¸ WindowEvent Operatiuni asupra ferestrelor: ¸ minimizare, maximizare,etc. O anumit˘ actiune a utilizatorului poate genera mai multe evenimente. a ¸De exemplu, tastarea literei ’A’ va genera trei evenimente: unul pentruap˘sare, unul pentru eliberare ¸i unul pentru tastare. In functie de nece- a s ¸sit˘¸ile aplicatie putem scrie cod pentru tratarea fiec˘rui eveniment ˆ parte. at ¸ a ın Evenimentele semantice reprezint˘ interactiunea cu o component˘ a ¸ aGUI: ap˘sarea unui buton, selectarea unui articol dintr-o list˘, etc. Clasele a acare descriu aceste tipuri de evenimente sunt: ActionEvent Actionare ¸ AdjustmentEvent Ajustarea unei valori ItemEvent Schimbarea st˘rii a TextEvent Schimbarea textului
  • 226. 9.4. TRATAREA EVENIMENTELOR 225 Urm˘torul tabel prezint˘ componentele AWT ¸i tipurile de evenimente a a sgenerate, prezentate sub forma interfetelor corespunz˘toare. Evident, eveni- ¸ amentele generate de o superclas˘, cum ar fi Component, se vor reg˘si ¸i pentru a a stoate subclasele sale. Component ComponentListener FocusListener KeyListener MouseListener MouseMotionListener Container ContainerListener Window WindowListener Button List ActionListener MenuItem TextField Choice Checkbox ItemListener List CheckboxMenuItem Scrollbar AdjustmentListener TextField TextListener TextArea Observati c˘ de¸i exist˘ o singur˘ clas˘ MouseEvent, exist˘ dou˘ interfete ¸ a s a a a a a ¸asociate MouseListener ¸i MouseMotionListener. Acest lucru a fost f˘cut s adeoarece evenimentele legate de deplasarea mouse-ului sunt generate foartefrecvent ¸i receptionarea lor poate avea un impact negativ asupra vitezei de s ¸executie, ˆ situatia cˆnd tratarea acestora nu ne intereseaz˘ ¸i dorim s˘ ¸ ın ¸ a a s atrat˘m doar evenimente de tip click, de exemplu. a Orice clas˘ care trateaz˘ evenimente trebuie s˘ implementeze obligatoriu a a ametodele interfetelor corespunz˘toare. Tabelul de mai jos prezint˘, pentru ¸ a afiecare interfat˘, metodele puse la dispozitie ¸i care trebuie implementate de ¸a sc˘tre clasa ascult˘tor. a a
  • 227. 226 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Interfat˘ ¸a Metode ActionListener actionPerformed(ActionEvent e) AdjustmentListener adjustmentValueChanged(AdjustmentEvent e) componentHidden(ComponentEvent e) ComponentListener componentMoved(ComponentEvent e) componentResized(ComponentEvent e) componentShown(ComponentEvent e) ContainerListener componentAdded(ContainerEvent e) componentRemoved(ContainerEvent e) FocusListener focusGained(FocusEvent e) focusLost(FocusEvent e) ItemListener itemStateChanged(ItemEvent e) keyPressed(KeyEvent e) KeyListener keyReleased(KeyEvent e) keyTyped(KeyEvent e) mouseClicked(MouseEvent e) mouseEntered(MouseEvent e) MouseListener mouseExited(MouseEvent e) mousePressed(MouseEvent e) mouseReleased(MouseEvent e) MouseMotionListener mouseDragged(MouseEvent e) mouseMoved(MouseEvent e) TextListener textValueChanged(TextEvent e) windowActivated(WindowEvent e) windowClosed(WindowEvent e) windowClosing(WindowEvent e) WindowListener windowDeactivated(WindowEvent e) windowDeiconified(WindowEvent e) windowIconified(WindowEvent e) windowOpened(WindowEvent e) In cazul ˆ care un obiect listener trateaz˘ evenimente de acela¸i tip provo- ın a scate de componente diferite, este necesar s˘ putem afla, ˆ cadrul uneia din a ınmetodele de mai sus, care este sursa evenimentului pe care ˆ trat˘m pen- ıl atru a putea reactiona ˆ consecint˘. Toate tipurile de evenimente mo¸tenesc ¸ ın ¸a smetoda getSource care returneaz˘ obiectul responsabil cu generarea eveni- amentului. In cazul ˆ care dorim s˘ diferentiem doar tipul componentei surs˘, ın a ¸ a
  • 228. 9.4. TRATAREA EVENIMENTELOR 227putem 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’ } ... } } Pe lˆng˘ getSource, obiectele ce descriu evenimente pot pune la dispozitie a a ¸¸i alte metode specifice care permit aflarea de informatii legate de evenimen-s ¸tul generat. De exemplu, ActionEvent contine metoda getActionCommand ¸care, implicit, returneaz˘ eticheta butonului care a fost ap˘sat. Astfel de a aparticularit˘¸i vor fi prezentate mai detaliat ˆ sectiunile dedicate fiecˆrei at ın ¸ acomponente ˆ parte. ın9.4.3 Folosirea adaptorilor ¸i a claselor anonime sAm vazut c˘ o clas˘ care trateaz˘ evenimente de un anumit tip trebuie s˘ im- a a a aplementeze interfata corespunz˘toare acelui tip. Aceasta ˆ ¸ a ınseamn˘ c˘ trebuie a as˘ implementeze obligatoriu toate metodele definite de acea interfat˘, chiar a ¸adac˘ nu specific˘ nici un cod pentru unele dintre ele. Sunt ˆ a situatii cˆnd a a ıns˘ ¸ aacest lucru poate deveni sup˘rator, mai ales atunci cˆnd nu ne intereseaz˘ a a adecˆt o singura metod˘ a interfetei. a a ¸ Un exemplu sugestiv este urm˘torul: o fereastr˘ care nu are specificat cod a apentru tratarea evenimentelor sale nu poate fi ˆ ınchis˘ cu butonul standard a
  • 229. 228 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸marcat cu ’x’ din coltul dreapta sus ¸i nici cu combinatia de taste Alt+F4. ¸ s ¸Pentru a realiza acest lucru trebuie interceptat evenimentul de ˆ ınchidere aferestrei ˆ metoda windoClosing ¸i apelat˘ metoda dispose de ˆ ın s a ınchiderea ferestrei, sau System.exit pentru terminarea programului, ˆ cazul cˆnd ın aeste vorba de fereastra principal˘ a aplicatiei. Aceasta ˆ a ¸ ınseamn˘ c˘ trebuie a as˘ implement˘m interfata WindowListener care are nu mai putin de ¸apte a a ¸ ¸ smetode. Listing 9.11: Implementarea interfetei WindowListener ¸import java . awt .*;import java . awt . event .*;class Fereastra extends Frame implements WindowListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( this ) ; } // Metodele interfetei WindowListener public void windowOpened ( WindowEvent e ) {} public void windowClosing ( WindowEvent e ) { // Terminare program System . exit (0) ; } public void windowClosed ( WindowEvent e ) {} public void windowIconified ( WindowEvent e ) {} public void windowDeiconified ( WindowEvent e ) {} public void windowActivated ( WindowEvent e ) {} public void windowDeact ivated ( WindowEvent e ) {}}public class TestWind ow Lis te ne r { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test WindowListener " ) ; f . show () ; }} Observati c˘ trebuie s˘ implement˘m toate metodele interfetei, chiar dac˘ ¸ a a a ¸ anu scriem nici un cod pentru unele dintre ele. Singura metod˘ care ne intere- aseaz˘ este windowClosing, ˆ care specific˘m ce trebuie f˘cut atunci cˆnd a ın a a autilizatorul doreste s˘ ˆ a ınchid˘ fereastra. Pentru a evita scrierea inutil˘ a a a
  • 230. 9.4. TRATAREA EVENIMENTELOR 229acestor metode, exist˘ o serie de clase care implementeaz˘ interfetele de tip a a ¸”listener” f˘r˘ a specifica nici un cod pentru metodele lor. Aceste clase se aanumesc adaptori. Un adaptor este o clas˘ abstract˘ care implementeaz˘ o anumit˘ interfat˘ a a a a ¸af˘r˘ a specifica cod nici unei metode a interfetei. aa ¸ Scopul unei astfel de clase este ca la crearea unui ”ascult˘tor” de eveni- amente, ˆ loc s˘ implement˘ o anumit˘ interfat˘ ¸i implicit toate metodele ın a a a ¸a ssale, s˘ extindem adaptorul corespunz˘tor interfetei respective (dac˘ are!) a a ¸ a¸i s˘ supradefinim doar metodele care ne intereseaz˘ (cele ˆ care vrem s˘s a a ın ascriem o anumit˘ secvent˘ de cod). a ¸a De exemplu, adaptorul interfetei WindowListener este WindowAdapter ¸iar folosirea acestuia este dat˘ ˆ exemplul de mai jos: a ın Listing 9.12: Extinderea clasei WindowAdapterimport 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˘¸i de tratare a evenimentelor este re- atducerea codului programului, acesta devenind mult mai lizibil. Ins˘ exist˘ ¸i a asdou˘ dezavantaje majore. Dup˘ cum ati observat fat˘de exemplul anterior, a a ¸ ¸a
  • 231. 230 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸clasa Fereastra nu poate extinde WindowAdapter deoarece ea extinde dejaclasa Frame ¸i din acest motiv am construit o nou˘ clas˘ numit˘ Ascultator. s a a aVom vedea ˆ a c˘ acest dezavantaj poate fi eliminat prin folosirea unei clase ıns˘ aanonime.Un alt dezavantaj este c˘ orice gre¸eal˘ de sintax˘ ˆ declararea unei metode a s a a ına interfetei nu va produce o eroare de compilare dar nici nu va supradefini ¸metoda interfetei ci, pur ¸i simplu, va crea o metod˘ a clasei respective. ¸ s aclass Ascultator extends WindowAdapter { // In loc de windowClosing scriem WindowClosing // Nu supradefinim vreo metoda a clasei WindowAdapter // Nu da nici o eroare // Nu face nimic ! public void WindowClosing(WindowEvent e) { System.exit(0); }} In tabelul de mai jos sunt dati toti adaptorii interfetelor de tip ”listener” ¸ ¸ ¸- se oberv˘ c˘ o interfat˘ XXXListener are un adaptor de tipul XXXAdapter. a a ¸aInterfetele care nu au un adaptor sunt cele care definesc o singur˘ metod˘ ¸i ¸ a asprin urmare crearea unei clase adaptor nu ˆ are rostul. ısi Interfata ¸ Adaptor ActionListener nu are AdjustemnrListener nu are ComponentListener ComponentAdapter ContainerListener ContainerAdapter FocusListener FocusAdapter ItemListener nu are KeyListener KeyAdapter MouseListener MouseAdapter MouseMotionListener MouseMotionAdapter TextListener nu are WindowListener WindowAdapter Stim c˘ o clas˘ intern˘ este o clas˘ declarat˘ ˆ cadrul altei clase, iar ¸ a a a a a ınclasele anonime sunt clase interne folosite pentru instantierea unui singur ¸obiect de un anumit tip. Un exemplu tipic de folosire a lor este instantierea ¸
  • 232. 9.4. TRATAREA EVENIMENTELOR 231adaptorilor direct ˆ corpul unei clase care contine componente ale c˘ror ın ¸ aevenimente trebuie tratate. Listing 9.13: Folosirea adaptorilor ¸i a claselor anonime simport java . awt .*;import java . awt . event .*;class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (400 , 400) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { // Terminam aplicatia System . exit (0) ; } }) ; final Label label = new Label ( " " , Label . CENTER ) ; label . setBackground ( Color . yellow ) ; add ( label , BorderLayout . NORTH ) ; this . addMouseListener ( new MouseAdapter () { public void mouseClicked ( MouseEvent e ) { // Desenam un cerc la fiecare click de mouse label . setText ( " Click ... " ) ; Graphics g = Fereastra . this . getGraphics () ; g . setColor ( Color . blue ) ; int raza = ( int ) ( Math . random () * 50) ; g . fillOval ( e . getX () , e . getY () , raza , raza ) ; } }) ; this . ad dM ous eM oti on Li st e n e r ( new Mo us eM ot ion Ad ap te r () { public void mouseMoved ( MouseEvent e ) { // Desenam un punct la coordonatele mouse - ului Graphics g = Fereastra . this . getGraphics () ; g . drawOval ( e . getX () , e . getY () , 1 , 1) ; } }) ;
  • 233. 232 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ this . addKeyListener ( new KeyAdapter () { public void keyTyped ( KeyEvent e ) { // Afisam caracterul tastat label . setText ( " Ati tastat : " + e . getKeyChar () + " " ) ; } }) ; }}public class TestAdapters { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test adaptori " ) ; f . show () ; }}9.5 Folosirea ferestrelorDup˘ cum am v˘zut suprafetele de afi¸are ale componentelor sunt extensii a a ¸ sale clasei Container. O categorie aparte a acestor containere o reprezint˘ aferestrele. Acestea sunt descrise de clase derivate din Window, cele maiutilizate fiind Frame ¸i Dialog. s O aplicatie Java cu intefat˘ grafic˘ va fi format˘ din una sau mai multe ¸ ¸a a aferestre, una dintre ele fiind numit˘ fereastra principal˘. a a9.5.1 Clasa WindowClasa Window este rar utilizat˘ ˆ mod direct deoarece permite doar crearea a ınunor ferestre care nu au chenar ¸i nici bar˘ de meniuri. Este util˘ atunci s a acˆnd dorim s˘ afi¸am ferestre care nu interactioneaz˘ cu utilizatorul ci doar a a s ¸ aofer˘ anumite informatii. a ¸ Metodele mai importante ale clasei Window, care sunt de altfel mo¸tenite sde toate subclasele sale, sunt date de mai jos: • show - face vizibil˘ fereastra. Implicit, o fereastr˘ nou creat˘ nu este a a a vizibil˘; a • hide - face fereastra invizibil˘ f˘r˘ a o distruge ˆ a; pentru a redeveni a aa ıns˘ vizibila se poate apela metoda show;
  • 234. 9.5. FOLOSIREA FERESTRELOR 233 • isShowing - testeaz˘ dac˘ fereastra este vizibil˘ sau nu; a a a • dispose - ˆ ınchide) fereastra ¸i ¸i elibereaz˘ toate resursele acesteia; s s a • pack - redimensioneaz˘ automat fereastra la o suprafata optim˘ care a ¸ a s˘ cuprind˘ toate componentele sale; trebuie apelat˘ ˆ general dup˘ a a a ın a ad˘ugarea tuturor componentelor pe suprafata ferestrei. a ¸ • getFocusOwner - returneaz˘ componenta ferestrei care are focus-ul a (dac˘ fereastra este activ˘). a a9.5.2 Clasa FrameEste derivat˘ a clasei Window ¸i este folosit˘ pentru crearea de ferestre inde- a s apendente ¸i functionale, eventual continˆnd o bar˘ de meniuri. Orice aplicatie s ¸ a a ¸cu interfat˘ grafic˘ conttine cel putin o fereastr˘, cea mai important˘ fiind ¸a a ¸ ¸ a anumit˘ ¸i fereastra principal˘. as a Constructorii uzuali ai clasei Frame permit crearea unei ferestre cu sauf˘r˘ titlu, initial invizibil˘. Pentru ca o fereastr˘ s˘ devin˘ vizibil˘ se va aa ¸ a a a a aapela metoda show definit˘ ˆ superclasa Window. a ınimport java.awt.*;public class TestFrame { public static void main(String args[]) { Frame f = new Frame("Titlul ferestrei"); f.show(); }} Crearea ferestrelor prin instantierea direct˘ a obiectelor de tip Frame este ¸ amai putin folosit˘. De obicei, ferestrele unui program vor fi definite ˆ clase ¸ a ınseparate care extind clasa Frame, ca ˆ exemplul de mai jos: ınimport java.awt.*;class Fereastra extends Frame{ // Constructorul public Fereastra(String titlu) { super(titlu); ... }
  • 235. 234 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸}public class TestFrame { public static void main(String args[]) { Fereastra f = new Fereastra("Titlul ferestrei"); f.show(); }} Gestionarul de pozitionare implicit al clasei Frame este BorderLayout. ¸Din acest motiv, ˆ momentul ˆ care fereastra este creat˘ dar nici o compo- ın ın anent˘ grafic˘ nu este ad˘ugat˘, suprafata de afi¸are a feretrei va fi determi- a a a a ¸ snat˘ automota de gestionarul de pozittionare ¸i va oferi doar spatiul necesar a ¸ s ¸afi¸˘rii barei ferestrei ¸i grupului de butoane pentru minimizare, maximizare sa s¸i ˆs ınchidere. Acela¸i efect ˆ vom obtine dac˘ o redimenionam ¸i apel˘m apoi s ıl ¸ a s ametoda pack care determin˘ dimeniunea suprafetei de afi¸are ˆ functie de a ¸ s ın ¸componentele ad˘ugate. a Se observ˘ de asemenea c˘ butonul de ˆ a a ınchidere a ferestrei nu este functional. ¸Tratarea evenimentelor ferestrei se face prin implementarea interfetei WindowListener ¸sau, mai uzual, prin folosirea unui adaptor de tip WindowAdapter. Structura general˘ a unei ferestre este descris˘ de clasa Fereastra din a aexemplul de mai jos: Listing 9.14: Structura general˘ a unei ferestre aimport java . awt .*;import java . awt . event .*;class Fereastra extends Frame implements ActionListener { // Constructorul public Fereastra ( String titlu ) { super ( titlu ) ; // Tratam evenimentul de inchidere a ferestrei this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { dispose () ; // inchidem fereastra // sau terminam aplicatia System . exit (0) ; } }) ;
  • 236. 9.5. FOLOSIREA FERESTRELOR 235 // 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 () ; }} Pe lˆng˘ metodele mo¸tenite din clasa Window, exist˘ o serie de metode a a s aspecifice clasei Frame. Dintre cele mai folosite amintim: • getFrames - metod˘ static˘ ce returneaz˘ lista tuturor ferestrelor de- a a a schise ale unei aplicatii; ¸ • setIconImage - seteaz˘ iconita ferestrei; a ¸ • setMenuBar - seteaz˘ bara de meniuri a ferestrei (vezi ”Folosirea me- a niurilor”); • setTitle - seteaz˘ titlul ferestrei; a
  • 237. 236 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ • setResizable - stabile¸te dac˘ fereastra poate fi redimenionat˘ de uti- s a a lizator;9.5.3 Clasa DialogToate interfetele grafice ofer˘ un tip special de ferestre destinate prelu˘rii ¸ a aunor informatii sau a unor date de la utilizator. Acestea se numesc ferestre ¸de dialog sau casete de dialog ¸i sunt implementate prin intermediul clasei sDialog, subclas˘ direct˘ a clasei Window. a a Diferenta major˘ dintre ferestrele de dialog ¸i ferestrele de tip Frame ¸ a sconst˘ ˆ faptul c˘ o fereastr˘ de dialog este dependent˘ de o alt˘ fereastra a ın a a a a(normal˘ sau tot fereastr˘ de dialog), numit˘ ¸i fereastra p˘rinte. Cu alte a a as acuvinte, ferestrele de dialog nu au o existent˘ de sine st˘t˘toare. Cˆnd fer- ¸a aa aeastra p˘rinte este distrus˘ sunt distruse ¸i ferestrele sale de dialog, cˆnd este a a s aminimizat˘ ferestrele sale de dialog sunt f˘cute invizibile iar cˆnd este restau- a a arat˘ acestea sunt aduse la starea ˆ care se g˘seau ˆ momentul minimiz˘rii a ın a ın aferestrei p˘rinte. a Ferestrele de dialog pot fi de dou˘ tipuri: a • modale: care blocheaz˘ accesul la fereastra parinte ˆ momentul de- a ın schiderii lor, cum ar fi ferestrele de introducere a unor date, de alegere a unui fi¸ier, de selectare a unei optiuni, mesaje de avertizare, etc; s ¸ • nemodale: care nu blocheaz˘ fluxul de intrare c˘tre fereastra p˘rinte a a a - de exemplu, dialogul de c˘utare a unui cuvˆnt ˆ a a ıntr-un fi¸ier, etc. s Implicit, o fereastr˘ de dialog este nemodal˘ ¸i invizibil˘, ˆ a exist˘ con- a as a ıns˘ astructori care s˘ specifice ¸i ace¸ti parametri. Constructorii clasei Dialog a s ssunt: 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)
  • 238. 9.5. FOLOSIREA FERESTRELOR 237 Parametrul ”p˘rinte” reprezint˘ referinta la fereastra p˘rinte, ”titlu” a a ¸ areprezint˘ titlul ferestrei iar prin argumentul ”modal˘” specific˘m dac˘ fer- a a a aeastra de dialog creat˘ va fi modal˘ (true) sau nemodal˘ (false - valoarea a a aimplicit˘). a Crearea unei ferestre de dialog este relativ simpla ¸i se realizeaz˘ prin s aderivarea clasei Dialog. Comunicarea dintre fereastra de dialog ¸i fereastra ssa p˘rinte, pentru ca aceasta din urm˘ s˘ poat˘ folosi datele introduse (sau a a a aoptiunea specificata) ˆ caseta de dialog, se poate realiza folosind una din ¸ ınurm˘toarele abord˘ri generale: a a • obiectul care reprezint˘ dialogul poate s˘ trateze evenimentele generate a a de componentele de pe suprafata s˘ ¸i s˘ seteze valorile unor variabile ¸ as a accesibile ale ferestrei p˘rinte ˆ momentul ˆ care dialogul este ˆ a ın ın ıncheiat; • obiectul care creeaz˘ dialogul (fereastra p˘rinte) s˘ se ˆ a a a ınregistreze ca ascult˘tor al evenimentelor de la butoanele care determin˘ ˆ a a ıncheierea dialogului, iar fereastra de dialog s˘ ofere metode publice prin care a datele introduse s˘ fie preluate din exterior; a S˘ cre˘m, de exemplu, o fereastr˘ de dialog modal˘ pentru introducerea a a a aunui ¸ir de caractere. Fereastra principal˘ a aplicatiei va fi p˘rintele casetei s a ade dialog, va primi ¸irul de caractere introdus ¸i ˆ va modifica titlul ca fiind s s ısiacesta. Deschiderea ferestrei de dialog se va face la ap˘sarea unui buton al aferestrei principale numit ”Schimba titlul”. Cele dou˘ ferestre vor ar˘ta ca a aˆ imaginile de mai jos:ın Fereastra principal˘ a Fereastra de dialog Listing 9.15: Folosirea unei ferestre de dialogimport java . awt .*;import java . awt . event .*;// Fereastra principalaclass FerPrinc extends Frame implements ActionListener {
  • 239. 238 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ public FerPrinc ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new FlowLayout () ) ; setSize (300 , 80) ; Button b = new Button ( " Schimba titlul " ) ; add ( b ) ; b . addActionListener ( this ) ; } public void actionPerformed ( ActionEvent e ) { FerDialog d = new FerDialog ( this , " Dati titlul " , true ) ; String titlu = d . raspuns ; if ( titlu == null ) return ; setTitle ( titlu ) ; }}// Fereastra de dialogclass FerDialog extends Dialog implements ActionListener { public String raspuns = null ; private TextField text ; private Button ok , cancel ; public FerDialog ( Frame parinte , String titlu , boolean modala ) { super ( parinte , titlu , modala ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { raspuns = null ; dispose () ; } }) ; text = new TextField ( " " , 30) ; add ( text , BorderLayout . CENTER ) ; Panel panel = new Panel () ; ok = new Button ( " OK " ) ;
  • 240. 9.5. FOLOSIREA FERESTRELOR 239 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 principalapublic class TestDialog { public static void main ( String args []) { FerPrinc f = new FerPrinc ( " Fereastra principala " ) ; f . show () ; }}9.5.4 Clasa FileDialogPachetul java.awt pune la dispozitie ¸i un tip de fereastr˘ de dialog folosit˘ s a apentru selectarea unui nume de fi¸ier ˆ vederea ˆ arc˘rii sau salv˘rii unui s ın ınc˘ a afi¸ier: clasa FileDialog, derivat˘ din Dialog. Instantele acestei clase au un s a ¸comportament comun dialogurilor de acest tip de pe majoritatea platformelorde lucru, dar forma ˆ care vor fi afi¸ate este specific˘ platformei pe care ın s aruleaz˘ aplicatia. a ¸ Constructorii clasei sunt: FileDialog(Frame parinte)
  • 241. 240 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ FileDialog(Frame parinte, String titlu) FileDialog(Frame parinte, String titlu, boolean mod)Parametrul ”p˘rinte” reprezint˘ referinta ferestrei p˘rinte, ”titlu” reprezint˘ a a ¸ a atitlul ferestrei iar prin argumentul ”mod” specific˘m dac˘ ˆ arc˘m sau a a ınc˘ asalv˘m un fi¸ier; valorile pe care le poate lua acest argument sunt: a s • FileDialog.LOAD - pentru ˆ arcare, respectiv ınc˘ • FileDialog.SAVE - pentru salvare.// Dialog pentru incarcarea unui fisiernew FileDialog(parinte, "Alegere fisier", FileDialog.LOAD);// Dialog pentru salvarea unui fisiernew FileDialog(parinte, "Salvare fisier", FileDialog.SAVE); La crearea unui obiect FileDialog acesta nu este implicit vizibil. Dac˘ aafi¸area sa se face cu show, caseta de dialog va fi modal˘. Dac˘ afi¸area s a a sse face cu setVisible(true), atunci va fi nemodal˘. Dup˘ selectarea unui a afi¸ier ea va fi facut˘ automat invizibil˘. s a a Pe lˆng˘ metodele mo¸tenite de la superclasa Dialog clasa FileDialog a a smai contine metode pentru obtinerea numelui fi¸ierului sau directorului se- ¸ ¸ slectat getFile, getDirectory, pentru stabilirea unui criteriu de filtraresetFilenameFilter, etc. S˘ consider˘m un exemplu ˆ care vom alege, prin intermediul unui obiect a a ınFileDialog, un fi¸ier cu extensia ”java”. Directorul initial este directorul s ¸curent, iar numele implicit este TestFileDialog.java. Numele fi¸ieruluisales va fi afi¸at la consol˘. s a Listing 9.16: Folosirea unei ferestre de dialogimport java . awt .*;import java . awt . event .*;import java . io .*;class FerPrinc extends Frame implements ActionListener { public FerPrinc ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () {
  • 242. 9.5. FOLOSIREA FERESTRELOR 241 public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; Button b = new Button ( " Alege fisier " ) ; add (b , BorderLayout . CENTER ) ; pack () ; b . addActionListener ( this ) ; } public void actionPerformed ( ActionEvent e ) { FileDialog fd = new FileDialog ( this , " Alegeti un fisier " , FileDialog . LOAD ) ; // Stabilim directorul curent fd . setDirectory ( " . " ) ; // Stabilim numele implicit fd . setFile ( " TestFileDialog . java " ) ; // Specificam filtrul fd . setFilenameFilter ( new FilenameFilter () { public boolean accept ( File dir , String numeFis ) { return ( numeFis . endsWith ( " . java " ) ) ; } }) ; // Afisam fereastra de dialog ( modala ) fd . show () ; System . out . println ( " Fisierul ales este : " + fd . getFile () ) ; }}public class TestFileDialog { public static void main ( String args []) { FerPrinc f = new FerPrinc ( " Fereastra principala " ) ; f . show () ; }} Clasa FileDialog este folosit˘ mai rar deoarece ˆ Swing exist˘ clasa a ın aJFileChooser care ofer˘ mai multe facilit˘¸i ¸i prin urmare va constitui a at sprima optiune ˆ ¸ ıntr-o aplicatie cu intefat˘ grafic˘. ¸ ¸a a
  • 243. 242 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸9.6 Folosirea meniurilorSpre deosebire de celelalte obiecte grafice care deriv˘ din clasa Component, acomponentele unui meniu reprezint˘ instante ale unor clase derivate din su- a ¸perclasa abstract˘ MenuComponent. Aceast˘ exceptie este facut˘ deoarece a a ¸ aunele platforme grafice limiteaz˘ capabilit˘¸ile unui meniu. a at Meniurile pot fi grupate ˆ dou˘ categorii: ın a • Meniuri fixe (vizibile permanent): sunt grupate ˆ ıntr-o bar˘ de meniuri a ce contine cˆte un meniu pentru fiecare intrare a sa. La rˆndul lor, ¸ a a aceste meniuri contin articole ce pot fi selectate, comutatoare sau alte ¸ meniuri (submeniuri). O fereastr˘ poate avea un singur meniu fix. a • Meniuri de context (popup): sunt meniuri invizbile asociate unei ferestre ¸i care se activeaz˘ uzual prin ap˘sarea butonului drept al s a a mouse-ului. O alt˘ diferent˘ fat˘ de meniurile fixe const˘ ˆ faptul a ¸a ¸a a ın c˘ meniurile de context nu sunt grupate ˆ a ıntr-o bar˘ de meniuri. a In figura de mai jos este pus˘ ˆ evident˘ alc˘tuirea unui meniu fix: a ın ¸a a Exemplul de mai sus contine o bar˘ de meniuri format˘ din dou˘ meniuri ¸ a a aprincipale File ¸i Edit. Meniul Edit contine la rˆndul lui alt meniu (submeniu) s ¸ aOptions, articolul Undo ¸i dou˘ comutatoare Bold ¸i Italic. Prin abuz de s a slimbaj, vom referi uneori bara de meniuri a unei ferestre ca fiind meniulferestrei. In modelul AWT obiectele care reprezint˘ bare de meniuri sunt reprezen- atate ca instante al clasei MenuBar. Un obiect de tip MenuBar contine obiecte ¸ ¸de tip Menu, care sunt de fapt meniurile derulante propriu-zise. La rˆndulalor, acestea pot contine obiecte de tip MenuItem, CheckBoxMenuItem, ¸dar ¸i alte obiecte de tip Menu (submeniuri). s
  • 244. 9.6. FOLOSIREA MENIURILOR 243 Pentru a putea contine un meniu, o component˘ trebuie s˘ implementeze ¸ a ainterfata MenuContainer. Cel mai adesea, meniurile sunt ata¸ate fere- ¸ sstrelor, mai precis obiectelor de tip Frame, acestea implementˆnd interfat˘ a ¸aMenuContainer. Ata¸area unei bare de meniuri la o fereastr˘ se face prin s ametoda addMenuBar a clasei Frame.9.6.1 Ierarhia claselor ce descriu meniuriS˘ vedem ˆ continuare care este ierarhia claselor folosite ˆ lucrul cu meniuri a ın ın¸i s˘ analiz˘m pe rˆnd aceste clase:s a a a Clasa MenuComponent este o clasa abstract˘ din care sunt extinse atoate celelalte clase folosite la crearea de meniuri, fiind similar˘ celeilalte asuperclase abstracte Component. MenuComponent contine metode cu carac- ¸ter general, dintre care amintim getName, setName, getFont, setFont,cu sintaxa ¸i semnificatiile uzuale. s ¸ Clasa MenuBar permite crearea barelor de meniuri asociate unei ferestrecadru de tip Frame, adaptˆnd conceptul de bar˘ de meniuri la platforma a acurent˘ de lucru. Dup˘ cum am mai spus, pentru a lega bara de meniuri la a ao anumit˘ fereastra trebuie apelat˘ metoda setMenuBar din clasa Frame. a a// Crearea barei de meniuriMenuBar mb = new MenuBar();
  • 245. 244 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸// Adaugarea meniurilor derulante la bara de meniuri...// Atasarea barei de meniuri la o fereastraFrame f = new Frame("Fereastra cu meniu");f.addMenuBar(mb); Orice articol al unui meniu trebuie s˘ fie o instanta a clasei Menu- a ¸Item. Obiectele acestei clase descriu a¸adar optiunile individuale ale me- s ¸niurilor derulante, cum sunt ”Open”, ”Close”, ”Exit”, etc. O instant˘ a ¸aclasei MenuItem reprezint˘ de fapt un buton sau un comutator, cu o anumit˘ a aetichet˘ care va ap˘rea ˆ meniu, ˆ ¸it˘ eventual de un accelerator (obiect a a ın ınsot ade tip MenuShortcut) ce reprezint˘ combinatia de taste cu care articolul a ¸poate fi apelat rapid (vezi ”Acceleratori”). Clasa Menu permite crearea unui meniu derulant ˆ ıntr-o bar˘ de meniuri. aOptional, un meniu poate fi declarat ca fiind tear-off, ceea ce ˆ ¸ ınseamn˘ c˘ a apoate fi deschis ¸i deplasat cu mouse-ul (dragged) ˆ s ıntr-o alt˘ pozitie decˆt a ¸ acea original˘ (”rupt” din pozitia sa). Acest mecanism este dependent de a ¸platform˘ ¸i poate fi ignorat pe unele dintre ele. Fiecare meniu are o etichet˘, as acare este de fapt numele s˘u ce va fi afi¸at pe bara de meniuri. Articolele a sdintr-un meniu trebuie s˘ apartin˘ clasei MenuItem, ceea ce ˆ a ¸ a ınseamn˘ c˘ pot a afi instante ale uneia din clasele MenuItem, Menu sau CheckboxMenuItem. ¸ Clasa CheckboxMenuItem implementeaz˘ ˆ a ıntr-un meniu articole detip comutator - care au dou˘ st˘ri logice (validat/nevalidat), actionarea ar- a a ¸ticolului determinˆnd trecerea sa dintr-o stare ˆ alta. La validarea unui a ıncomutator ˆ dreptul etichetei sale va fi afi¸at un simbol grafic care indic˘ ın s aacest lucru; la invalidarea sa, simbolul grafic respectiv va disp˘rea. Clasa aCheckboxMenuItem are aceea¸i functionalitate cu cea a casetelor de validare s ¸de tip Checkbox, ambele implementˆnd interfata ItemSelectable. a ¸
  • 246. 9.6. FOLOSIREA MENIURILOR 245 S˘ vedem ˆ continuare cum ar ar˘ta un program care construie¸te un a ın a smeniu ca ˆ figura prezentat˘ anterior: ın a Listing 9.17: Crearea unui meniuimport 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 () ; }}
  • 247. 246 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸9.6.2 Tratarea evenimentelor generate de meniuriLa alegerea unei optiuni dintr-un meniu se genereaz˘ fie un eveniment de tip ¸ aActionEvent dac˘ articolul respectiv este de tip MenuItem, fie ItemEvent apentru comutatoarele CheckboxMenuItem. A¸adar, pentru a activa optiunile s ¸unui meniu trebuie implementate interfatele ActionListener sau/¸i Item- ¸ sListener ˆ cadrul obiectelor care trebuie s˘ specifice codul ce va fi executat ın ala alegerea unei optiuni ¸i implementate metodele actionPerformed, respec- ¸ stiv itemStatChanged. Fiec˘rui meniu ˆ putem asocia un obiect receptor a ıidiferit, ceea ce u¸ureaz˘ munca ˆ cazul ˆ care ierarhia de meniuri este com- s a ın ınplex˘. Pentru a realiza leg˘tura ˆ a a ıntre obiectul meniu ¸i obiectul de tip listener strebuie s˘ adaug˘m receptorul ˆ lista de ascult˘tori a meniului respectiv, a a ın aˆıntocmai ca pe orice component˘, folosind metodele addActionListener, arespectiv addItemListener. A¸adar, tratarea evenimentelor generate de obiecte de tip MenuItem este sidentic˘ cu tratarea butoanelor, ceea ce face posibil ca unui buton de pe asuprafata de afi¸are s˘ ˆ corespund˘ o optiune dintr-un meniu, ambele cu ¸ s a ıi a ¸acela¸i nume, tratarea evenimentului corespunz˘tor ap˘sarii butonului, sau s a aalegerii optiunii, f˘cˆndu-se o singur˘ dat˘ ˆ ¸ a a a a ıntr-o clas˘ care este ˆ a ınregistrat˘ aca receptor atˆt la buton cˆt ¸i la meniu. a a s Obiectele de tip CheckboxMenuItem tip se g˘sesc ˆ a ıntr-o categorie comun˘ acu List, Choice, CheckBox, toate implementˆnd interfata ItemSelectable a ¸¸i deci tratarea lor va fi f˘cut˘ la fel. Tipul de operatie selectare / deselectares a aeste codificat ˆ evenimentul generat de cˆmpurile statice ItemEvent.SELECTED ın a¸i ItemEvent.DESELECTED.s Listing 9.18: Tratarea evenimentelor unui meniuimport 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 " ) ;
  • 248. 9.6. FOLOSIREA MENIURILOR 247 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 () ; }}9.6.3 Meniuri de context (popup)Au fost introduse ˆ ıncepˆnd cu AWT 1.1 ¸i sunt implementate prin intermediul a sclasei PopupMenu, subclas˘ direct˘ a clasei Menu. Sunt meniuri invizibile a acare sunt activate uzual prin ap˘sarea butonului drept al mouse-ului, fiind a
  • 249. 248 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸afi¸ate la pozitia la care se g˘sea mouse-ul ˆ momentul ap˘s˘rii butonului s ¸ a ın aas˘u drept. Metodele de ad˘ugare a articolelor unui meniu de context sunt a amo¸tenite ˆ s ıntocmai de la meniurile fixe. PopupMenu popup = new PopupMenu("Options"); popup.add(new MenuItem("New")); popup.add(new MenuItem("Edit")); popup.addSeparator(); popup.add(new MenuItem("Exit"));Afi¸area meniului de context se face prin metoda show: s popup.show(Component origine, int x, int y)¸i este de obicei rezultatul ap˘sarii unui buton al mouse-ului, pentru a aveas aacces rapid la meniu. Argumentul ”origine” reprezint˘ componenta fat˘ de a ¸aoriginile c˘reia se va calcula pozitia de afi¸are a meniului popup. De obicei, a ¸ sreprezint˘ instanta ferestrei ˆ care se va afi¸a meniul. Deoarece interactiunea a ¸ ın s ¸cu mouse-ul este dependent˘ de platforma de lucru, exist˘ o metod˘ care a a adetermin˘ dac˘ un eveniment de tip MouseEvent poate fi responsabil cu a adeschiderea unui meniu de context. Aceasta este isPopupTrigger ¸i este sdefinit˘ ˆ clasa MouseEvent. Pozitionarea ¸i afi¸area meniului este ˆ a a ın ¸ s s ıns˘responsabilitatea programatorului. Meniurile de context nu se adaug˘ la un alt meniu (bar˘ sau sub-meniu) a aci se ata¸eaz˘ la o component˘ (de obicei la o fereastr˘) prin metoda add s a a aa acesteia. In cazul cˆnd avem mai multe meniuri popup pe care vrem s˘ a ale folosim ˆıntr-o fereastr˘, trebuie s˘ le definim pe toate ¸i, la un moment a a sdat, vom ad˘uga ferestrei meniul corespunz˘tor dup˘ care ˆ vom face vizibil. a a a ılDup˘ ˆ a ınchiderea acestuia, vom ”rupe” leg˘tura ˆ a ıntre fereastr˘ ¸i meniu prin asinstructiunea remove: ¸ fereastra.add(popup1); ... fereastra.remove(popup1); fereastra.add(popup2); In exemplul de mai jos, vom crea un meniu de contex pe care ˆ vom activa ılla ap˘sarea butonului drept al mouse-ului pe suprafata ferestrei principale. a ¸Tratarea evenimentelor generate de un meniu popup se realizeaz˘ identic ca apentru meniurile fixe.
  • 250. 9.6. FOLOSIREA MENIURILOR 249 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) ;
  • 251. 250 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ }}public class TestPopupMenu { public static void main ( String args []) { Fereastra f = new Fereastra ( " PopupMenu " ) ; f . show () ; }}9.6.4 Acceleratori (Clasa MenuShortcut)Pentru articolele unui menu este posibil˘ specificarea unor combinatii de a ¸taste numite acceleratori (shortcuts) care s˘ permit˘ accesarea direct˘, prin a a aintermediul tastaturii, a optiunilor dintr-un meniu. Astfel, oric˘rui obiect ¸ ade tip MenuItem ˆ poate fi asociat un obiect de tip accelerator, definit prin ıiintermediul clasei MenuShortcut. Singurele combinatii de taste care pot ¸juca rolul acceleratorilor sunt: Ctrl + Tasta sau Ctrl + Shift + Tasta.Atribuirea unui accelerator la un articol al unui meniu poate fi realizat˘ prin aconstructorul obiectelor de tip MenuItem ˆ forma: ınMenuItem(String eticheta, MenuShortcut accelerator), ca ˆ exempleleınde 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 AWTIn continuare vor fi date exemple de folosire ale componentelor AWT, ˆ care ıns˘ fie puse ˆ evidentt˘ cˆt mai multe din particularit˘¸ile acestora, precum a ın ¸a a at¸i modul de tratare a evenimentelor generate.s
  • 252. 9.7. FOLOSIREA COMPONENTELOR AWT 2519.7.1 Clasa LabelUn obiect de tip Label (etichet˘) reprezint˘ o component˘ pentru plasarea a a aunui text pe o suprafata de afi¸are. O etichet˘ este format˘ dintr-o singur˘ ¸ s a a alinie de text static ce nu poate fi modificat de c˘tre utilizator, dar poate fi amodificat din program. Listing 9.20: Folosirea clasei Labelimport 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 ) ;
  • 253. 252 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ f . add ( centru , BorderLayout . CENTER ) ; f . pack () ; f . show () ; }}9.7.2 Clasa ButtonUn obiect de tip Button reprezint˘ o component˘ pentru plasarea unui bu- a aton etichetat pe o suprafata de afi¸are. Textul etichetei este format dintr-o ¸ ssingur˘ linie. a Listing 9.21: Folosirea clasei Buttonimport java . awt .*;import java . awt . event .*;class Fereastra extends Frame implements ActionListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( null ) ; setSize (200 , 120) ; Button b1 = new Button ( " OK " ) ; b1 . setBounds (30 , 30 , 50 , 70) ; b1 . setFont ( new Font ( " Arial " , Font . BOLD , 14) ) ;
  • 254. 9.7. FOLOSIREA COMPONENTELOR AWT 253 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 () ; }}9.7.3 Clasa CheckboxUn obiect de tip Checkbox (comutator) reprezint˘ o component˘ care se a apoate g˘si ˆ dou˘ st˘ri: ”selectat˘” sau ”neselectat˘” (on/off). Actiunea a ın a a a a ¸utilizatorului asupra unui comutator ˆ trece pe acesta ˆ starea complemen- ıl ıntar˘ celei ˆ care se g˘sea. Este folosit pentru a prelua o anumit˘ optiune de a ın a a ¸la utilizator.
  • 255. 254 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Listing 9.22: Folosirea clasei Checkboximport java . awt .*;import java . awt . event .*;class Fereastra extends Frame implements ItemListener { private Label label1 , label2 ; private Checkbox cbx1 , cbx2 , cbx3 ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (5 , 1) ) ; label1 = new Label ( " Ingrediente Pizza : " , Label . CENTER ) ; label1 . setBackground ( Color . orange ) ; label2 = new Label ( " " ) ; label2 . setBackground ( Color . lightGray ) ; cbx1 = new Checkbox ( " ca