Curs practic de Java

   Cristian Fr˘sinaru
              a
Cuprins

1 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
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 . . . . . . . . . . . . .        .   .   .   .   .   .   34

2 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
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 . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   75

3 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 . . . . . . . .
                               ¸                       . . . . .           .   .   .   .   .   .   .   92

4 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
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 . . . . . . . . . . . . . . . . . . . . . . . . . . .                     .   .   119

5 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 . . . . . . . . . . . . . . . . . . . .         .   .   .   .   .   .   .   .   .   . 142

6 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
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 . . . . . . . .
                                 ¸                                                            .   .   .   .   .   159

7 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               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . 172

8 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 . . . . . . . . . . . . . . .                       .   .   .   .   .   .   .   .   .   196

9 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
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 . . . . . . . . . . . . . . . .             .   .   .   .   .   .   265

10 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
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                                             .   .   .   .   .   .   .   .   .   .   .   293

11 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
8                                                                                        CUPRINS

12 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                                     .   .   .   .   .   .   .   .   .   .   . 378

13 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             ¸      ¸           .   .   .   .   .   .   .   .   .   . 397

14 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
CUPRINS                                                                                             9

15 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 . . . .
                      ¸                                      .   .   .   .   .   .   .   .   .   . 443

16 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
10   CUPRINS
Capitolul 1

Introducere ˆ Java
            ın

1.1      Ce este Java ?
Java este o tehnologie inovatoare lansat˘ de compania Sun Microsystems ˆ
                                         a                                  ın
1995, care a avut un impact remarcabil asupra ˆ    ıntregii comunit˘¸i a dez-
                                                                   at
voltatorilor de software, impunˆndu-se prin calit˘¸i deosebite cum ar fi sim-
                                a                 at
plitate, 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 pe
baza c˘ruia sunt construite o serie de platforme destinate implement˘rii de
       a                                                               a
aplicatii pentru toate segmentele industriei software.
      ¸


1.1.1     Limbajul de programare Java
Inainte de a prezenta ˆ detaliu aspectele tehnice ale limbajului Java, s˘ am-
                        ın                                                 a
intim caracteristicile sale principale, care l-au transformat ˆ
                                                              ıntr-un interval de
timp 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
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               s

1.1.2      Platforme de lucru Java
Limbajul de programare Java a fost folosit la dezvoltarea unor tehnologii ded-
icate rezolv˘rii unor probleme din cele mai diverse domenii. Aceste tehnologii
             a
au fost grupate ˆ a¸a numitele platforme de lucru, ce reprezint˘ seturi de
                   ın s                                            a
libr˘rii scrise ˆ limbajul Java, precum ¸i diverse programe utilitare, folosite
    a           ın                       s
pentru dezvoltarea de aplicatii sau componente destinate unei anume cate-
                              ¸
gorii de utilizatori.
1.1. CE ESTE JAVA ?                                                         13

   • J2SE (Standard Edition)
     Este platforma standard de lucru ce ofer˘ suport pentru crearea de
                                             a
     aplicatii independente ¸i appleturi.
           ¸                s
     De asemenea, aici este inclus˘ ¸i tehnologia Java Web Start ce furnizeaz˘
                                  as                                           a
     o modalitate extrem de facil˘ pentru lansarea ¸i instalarea local˘ a 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             a
Internet de la adresa ”http://java.sun.com”.
    In continuare, vom folosi termenul J2SDK pentru a ne referi la distributia
                                                                           ¸
standard J2SE 1.5 SDK (Tiger).

1.1.3    Java: un limbaj compilat ¸i interpretat
                                  s
In functie de modul de executie a aplicatiilor, limbajele de programare se
       ¸                    ¸           ¸
ˆ
ımpart ˆ dou˘ categorii:
       ın     a
   • Interpretate: instructiunile sunt citite linie cu linie de un program
                             ¸
     numit interpretor ¸i traduse ˆ instructiuni 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
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 s
de 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 a
tat de o succesiune de instructiuni specifice unui anumit procesor ¸i unei an-
                                 ¸                                      s
umite platforme de lucru reprezentate ˆ format binar astfel ˆ at s˘ poat˘
                                             ın                     ıncˆ a       a
fi 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                       s
de lucru. In timp ce codul ma¸in˘ este executat direct de c˘tre procesor ¸i
                                   s a                            a              s
poate 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                                              a
care 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     a
anumit calculator, pe acesta trebuie s˘ fie instalat˘ o ma¸in˘ virtual˘ Java.
                                           a            a     s a         a
Acest lucru este realizat automat de c˘tre distributia J2SDK.
                                           a            ¸


1.2      Primul program
Crearea oric˘rei aplicatii Java presupune efectuarea urm˘torilor pa¸i:
            a          ¸                                a          s


     1. Scriererea codului surs˘
                               a



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

    Toate aplicatiile Java contin o clas˘ principal˘(primar˘) ˆ care trebuie
                ¸             ¸         a          a       a ın
s˘ se gaseasc˘ metoda main. Clasele aplicatiei se pot gasi fie ˆ
 a           a                               ¸                 ıntr-un singur
fi¸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                                                  a
sie nefiind acceptat˘. Este recomandat ca fi¸ierul care contine codul surs˘
                     a                       s              ¸               a
al clasei primare s˘ aib˘ acela¸i nume cu cel al clasei, de¸i acest lucru nu
                   a     a     s                           s
este obligatoriu. S˘ presupunem c˘ am salvat exemplul de mai sus ˆ fi¸ierul
                   a              a                                 ın s
C: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           ¸                  a
aplicatiei sau pentru orice fi¸ier/fi¸iere cu extensia java. Compilatorul creeaz˘
      ¸                      s      s                                         a
cˆ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.java
In 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
       a
intrare 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                       s
astfel:
  java FirstApp
Rularea unei aplicatii care nu folose¸te interfat˘ grafic˘, se va face ˆ
                   ¸                 s          ¸a      a             ıntr-o
fereastr˘ sistem.
        a
16                              CAPITOLUL 1. INTRODUCERE ˆ JAVA
                                                         IN


     Atentie
         ¸
     Un apel de genul java c:introFirstApp.class este gre¸it!
                                                           s




1.3       Structura lexical˘ a limbajului Java
                           a
1.3.1      Setul de caractere
Limbajului Java lucreaz˘ ˆ mod nativ folosind setul de caractere Unicode.
                          a ın
Acesta este un standard international care ˆ
                                 ¸          ınlocuie¸te vechiul set de caractere
                                                    s
ASCII ¸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 a
era posibil˘ reprezentarea a doar 256 de caractere. Primele 256 caractere
            a
Unicode corespund celor ASCII, referirea la celelalte f˘cˆndu-se prin uxxxx,
                                                        a a
unde xxxx reprezint˘ codul caracterului.
                     a
    O alt˘ caracteristic˘ a setului de caractere Unicode este faptul c˘ ˆ
          a             a                                               a ıntreg
intervalul de reprezentare a simbolurilor este divizat ˆ subintervale numite
                                                        ın
blocuri, cˆteva exemple de blocuri fiind: Basic Latin, Greek, Arabic, Gothic,
            a
Currency, 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 cheie
Cuvintele rezervate ˆ Java sunt, cu cˆteva exceptii, cele din C++ ¸i au fost
                    ın               a          ¸                 s
enumerate ˆ tabelul de mai jos. Acestea nu pot fi folosite ca nume de clase,
           ın
˘
1.3. STRUCTURA LEXICALA A LIMBAJULUI JAVA                                     17

interfete, variabile sau metode. true, false, null nu sunt cuvinte cheie,
       ¸
dar nu pot 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      a


1.3.3     Identificatori
Sunt secvente nelimitate de litere ¸i cifre Unicode, ˆ
           ¸                       s                 ıncepˆnd cu o liter˘. Dup˘
                                                          a             a     a
cum am mai spus, identificatorii nu au voie s˘ fie identici cu cuvintele rezer-
                                                a
vate.


1.3.4     Literali
Literalii 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).
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
˘
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 ın

1.3.5    Separatori
Un 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        a

1.3.6    Operatori
Operatorii Java sunt, cu mici deosebiri, cei din C++:

   • atribuirea: =

   • operatori matematici: +, -, *, /, %, ++, -- .
     Este permis˘ notatia prescurtat˘ de forma lval op= rval: x += 2 n
                 a      ¸              a
     -= 3
     Exist˘ operatori pentru autoincrementare ¸i autodecrementare (post ¸i
          a                                      s                          s
     pre): x++, ++x, n--, --n
     Evaluarea expresiilor logice se face prin metoda scurtcircuitului: 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
20                               CAPITOLUL 1. INTRODUCERE ˆ JAVA
                                                          IN

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

1.3.7      Comentarii
In Java exist˘ trei feluri de comentarii:
             a
     • Comentarii pe mai multe linii, ˆ
                                      ınchise ˆ
                                              ıntre /* ¸i */.
                                                       s
     • Comentarii pe mai multe linii care ¸in de documentatie, ˆ
                                           t                ¸ ınchise ˆ
                                                                      ıntre
       /** ¸i */. Textul dintre cele dou˘ secvente este automat mutat ˆ
           s                              a        ¸                     ın
       documentatia aplicatiei de 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 */.
1.4. TIPURI DE DATE SI VARIABILE
                    ¸                                                          21

1.4      Tipuri de date ¸i variabile
                        s
1.4.1     Tipuri de date
In Java tipurile de date se impart ˆ dou˘ categorii: tipuri primitive ¸i
                                      ın     a                              s
tipuri referint˘. Java porne¸te de la premiza c˘ ”orice este un obiect”,
                ¸a               s                   a
prin urmare tipurile de date ar trebui s˘ fie de fapt definite de clase ¸i toate
                                          a                             s
variabilele 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˘               ¸           a
exist˘ ¸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)
                          s

In alte limbaje de programare formatul ¸i dimensiunea tipurilor primitive de
                                        s
date pot depinde de platforma pe care ruleaz˘ programul. In Java acest lucru
                                            a
nu mai este valabil, orice dependent˘ de o anumit˘ platform˘ specific˘ fiind
                                   ¸a             a         a        a
eliminat˘.
         a


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


    Exist˘ trei tipuri de date din limbajul C care nu sunt suportate de lim-
          a
bajul Java. Acestea sunt: pointer, struct ¸i union. Pointerii au fost
                                                  s
eliminati din cauz˘ c˘ erau o surs˘ constant˘ de erori, locul lor fiind luat de
        ¸           a a             a         a
tipul referint˘, iar struct ¸i union nu ˆsi mai au rostul atˆt timp cˆt tipurile
             ¸a             s           ı¸                  a        a
compuse de date sunt formate ˆ Java prin intermediul claselor.
                                 ın
22                              CAPITOLUL 1. INTRODUCERE ˆ JAVA
                                                         IN

1.4.2      Variabile
Variabilele pot fi de tip primitiv sau referinte la obiecte (tip referint˘). In-
                                               ¸                         ¸a
diferent de tipul lor, pentru a putea fi folosite variabilele trebuie declarate ¸i,
                                                                               s
eventual, initializate.
             ¸

     • Declararea variabilelor: Tip numeVariabila;

     • Initializarea variabilelor: Tip numeVariabila = valoare;
          ¸

     • Declararea constantelor: final Tip numeVariabila;

   Evident, exist˘ posibilitatea de a declara ¸i initializa mai multe variabile
                 a                             s    ¸
sau constante de 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                                 a
criterii:

     • 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   a
categorii:

     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”).
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
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 a
din 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-else

if (expresie-logica) {
  ...
}

if (expresie-logica) {
   ...
} else {
   ...
}

     switch-case

switch (variabila) {
  case valoare1:
     ...
    break;
  case valoare2:
1.5. CONTROLUL EXECUTIEI
                    ¸                                                      25

      ...
      break;
    ...
    default:
      ...
}

   Variabilele care pot fi testate folosind instructiunea switch nu pot fi decˆt
                                                  ¸                         a
de tipuri primitive.

1.5.2         Instructiuni de salt
                     ¸
for

for(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


      while

while (expresie-logica) {
  ...
}



      do-while

do {
  ...
}
while (expresie-logica);
26                               CAPITOLUL 1. INTRODUCERE ˆ JAVA
                                                          IN

1.5.3      Instructiuni pentru tratarea exceptiilor
                  ¸                          ¸
Instructiunile pentru tratarea exceptiilor sunt try-catch-finally, respectiv
       ¸                            ¸
throw ¸i vor 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                    ın
de genul: break numeEticheata sau continue numeEticheta, utile pentru
a controla punctul de ie¸ire dintr-o structur˘ repetitiv˘, ca ˆ
                         s                   a           a      ınexemplul de
mai jos:
i=0;
eticheta:
while (i < 10) {
  System.out.println("i="+i);
  j=0;
  while (j < 10) {
    j++;
    if (j==5) continue eticheta;
    if (j==7) break eticheta;
    System.out.println("j="+j);
  }
  i++;
}


1.6       Vectori
1.6.1      Crearea unui vector
Crearea unui vector presupune realizarea urm˘toarelor etape:
                                            a
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];
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 genul
mente vor fi cuprinse ˆ       s                                   ¸
Tip numeVector[nrElemente], alocarea memoriei f˘cˆndu-se doar prin in-
                                                     a a
termediul opearatorului new.
     int v[10];                   //ilegal
     int v[] = new int[10];       //corect

1.6.2      Tablouri multidimensionale
In Java tablourile multidimensionale sunt de fapt vectori de vectori. De
exemplu, 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           a
elemente iar matrice[i][j] este elementul de pe linia i ¸i coloana j.
                                                         s

1.6.3      Dimensiunea unui vector
Cu ajutorul variabilei length se poate afla num˘rul de elemente al unui
                                              a
vector.
     int []a = new int[5];
     // a.length are valoarea 5

     int m[][] = new int[5][10];
     // m[0].length are valoarea 10
Pentru a ˆ ¸elege modalitatea de folosire a lui length trebuie mentionat c˘
         ınt                                                        ¸        a
fiecare vector este de fapt o instant˘ a unei clase iar length este o variabil˘
                                   ¸a                                        a
public˘ a acelei clase, ˆ care este retinut num˘rul maxim de elemente al
      a                 ın             ¸          a
vectorului.
1.6. VECTORI                                                              29

1.6.4    Copierea vectorilor
Copierea elementelor unui vector a ˆıntr-un alt vector b se poate face, fie
element cu element, fie cu ajutorul metodei System.arraycopy, ca ˆ exem-
                                                                 ın
plele de mai jos. Dup˘ cum vom vedea, o atribuire de genul b = a are alt˘
                     a                                                   a
semnificatie decˆt copierea elementelor lui a ˆ b ¸i nu poate fi folosit˘ ˆ
         ¸      a                            ın s                     a ın
acest scop.

  int a[] = {1, 2, 3, 4};
  int b[] = new int[4];

  // Varianta 1
  for(int i=0; i<a.length; i++)
    b[i] = a[i];

  // Varianta 2
  System.arraycopy(a, 0, b, 0, a.length);

  // Nu are efectul dorit
  b = a;

1.6.5    Sortarea vectorilor - clasa Arrays
In Java s-a pus un accent deosebit pe implementarea unor structuri de date
¸i algoritmi care s˘ simplifice proceseul de crearea a unui algoritm, program-
s                  a
atorul trebuind s˘ se concentreze pe aspectele specifice problemei abordate.
                  a
Clasa java.util.Arrays ofer˘ diverse metode foarte utile ˆ lucrul cu vec-
                                a                             ın
tori 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;
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                                          a

1.6.6      Vectori cu dimensiune variabil˘ ¸i eterogeni
                                         as
Implement˘ri ale vectorilor cu num˘r variabil de elemente sunt oferite de
           a                            a
clase 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
               s
din elemente de tip char, un obiect de tip String sau un obiect de tip
StringBuffer.
    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 aceste
clase este c˘ StringBuffer pune la dispozitie metode pentru modificarea
            a                                    ¸
continutului ¸irului, cum ar fi: append, insert, delete, reverse.
    ¸         s
Uzual, cea mai folosit˘ modalitate de a lucra cu ¸iruri este prin intermediul
                       a                             s
clasei String, care are ¸i unele particularit˘¸i fat˘ de restul claselor menite s˘
                        s                    at ¸a                               a
simplifice cˆt mai mult folosirea ¸irurilor de caractere. Clasa StringBuffer
            a                      s
va fi utilizat˘ predominant ˆ aplicatii dedicate proces˘rii textelor cum ar fi
             a               ın        ¸                   a
editoarele de texte.
    Exemple echivalente de declarare a unui ¸ir:s

     String s = "abc";
     String s = new String("abc");
     char data[] = {’a’, ’b’, ’c’};
     String s = new String(data);

    Observati prima variant˘ de declarare a ¸irului s din exemplul de mai sus
             ¸                a                s
- de altfel, cea mai folosit˘ - care prezint˘ o particularitate a clasei String
                            a                a
fata de restul claselor Java referitoare la instantierea obiectelor sale.
  ¸                                               ¸
˘
1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA 31

   Concatenarea ¸irurilor de caractere se face prin intermediul operatorului
                  s
+ sau, ˆ cazul ¸irurilor de tip StringBuffer, folosind metoda append.
       ın      s

  String s1 = "abc" + "xyz";
  String s2 = "123";
  String s3 = s1 + s2;

In Java, operatorul de concatenare + este extrem de flexibil, ˆ sensul c˘
                                                                 ın         a
permite concatenarea ¸irurilor cu obiecte de orice tip care au o reprezentare
                       s
de tip ¸ir de caractere. Mai jos, sunt 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 adunare
iar al doilea +, cel de concatenare a ¸irurilor.
                                      s


1.8     Folosirea argumentelor de la linia de co-
        mand˘ a
1.8.1    Transmiterea argumentelor
O aplicatie Java poate primi oricˆte argumente de la linia de comanda ˆ
         ¸                         a                                      ın
momentul lans˘rii ei. Aceste argumente sunt utile pentru a permite utiliza-
               a
torului s˘ specifice diverse optiuni legate de functionarea aplicatiei sau s˘
         a                     ¸                  ¸              ¸         a
furnizeze anumite date initiale programului.
                          ¸


   Atentie
        ¸
   Programele care folosesc argumente de la linia de comand˘ nu sunt 100%
                                                           a
pure Java, deoarece unele sisteme de operare, cum ar fi Mac OS, nu au ˆ  ın
mod normal linie de comand˘.a
32                             CAPITOLUL 1. INTRODUCERE ˆ JAVA
                                                        IN




    Argumentele de la linia de comand˘ sunt introduse la lansarea unei aplicatii,
                                     a                                       ¸
fiind specificate dup˘ numele aplicatiei ¸i separate prin spatiu. De exemplu,
                    a               ¸ s                      ¸
s˘ presupunem c˘ aplicatia Sortare ordoneaz˘ lexicografic (alfabetic) liniile
 a               a       ¸                    a
unui 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                               ¸             a
astfel:
     java Sortare persoane.txt
A¸adar, formatul general pentru lansarea unei aplicatii care prime¸te argu-
 s                                                  ¸             s
mente de la linia de comand˘ este:
                           a
     java NumeAplicatie [arg0 arg1 . . . argn]
    In cazul ˆ care sunt mai multe, argumentele trebuie separate prin spatii
             ın                                                           ¸
iar dac˘ unul dintre argumente contine spatii, atunci el trebuie pus ˆ
        a                            ¸       ¸                         ıntre
ghilimele. Evident, o aplicatie poate s˘ nu primeasc˘ nici un argument sau
                            ¸          a             a
poate s˘ ignore argumentele primite de la linia de comand˘.
        a                                                 a

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


   Vectoru args este instantiat cu un num˘r de elemente egal cu num˘rul ar-
                            ¸             a                        a
gumentelor primite de la linia de comand˘. A¸adar, pentru a afla num˘rul de
                                        a s                        a
argumente primite de program este suficient s˘ afl˘m dimensiunea vectorului
                                            a a
args prin intermediul atributului length:
˘
1.8. FOLOSIREA ARGUMENTELOR DE LA LINIA DE COMANDA 33

  public static void main (String args[]) {
    int numarArgumente = args.length ;
  }

    In cazul ˆ care aplicatia presupune existenta unor argumente de la linia
             ın            ¸                   ¸
de comand˘, ˆ a acestea nu au fost transmise programului la lansarea sa, vor
            a ıns˘
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    a
de la linia de comand˘ necesare pentru functionarea sa ¸i, ˆ caz contrar, s˘
                       a                     ¸           s ın               a
afi¸eze un mesaj de avertizare sau s˘ foloseasc˘ ni¸te valori implicite, ca ˆ
  s                                    a       a s                         ın
exemplul de mai jos:

public class Salut {
  public static void main (String args[]) {
    if (args.length == 0) {
      System.out.println("Numar insuficient de argumente!");
      System.exit(-1); //termina aplicatia
    }
    String nume = args[0]; //exista sigur
    String prenume;
    if (args.length >= 1)
      prenume = args[1];
    else
      prenume = ""; //valoare implicita
    System.out.println("Salut " + nume + " " + prenume);
  }
}

   Spre deosebire de limbajul C, vectorul primit de metoda main nu contine¸
pe prima pozitie numele aplicatiei, ˆ
              ¸                  ¸    ı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    s
pe ecran a argumentelor primite de la linia de comand˘:  a

public class Afisare {
  public static void main (String[] args) {
    for (int i = 0; i < args.length; i++)
      System.out.println(args[i]);
34                             CAPITOLUL 1. INTRODUCERE ˆ JAVA
                                                        IN

     }
}
    Un apel de genul java Afisare Hello Java va produce urm˘torul rezul-
                                                           a
tat (aplicatia a primit 2 argumente):
           ¸
     Hello
     Java
   Apelul java Afisare "Hello Java" va produce ˆ a alt rezultat (aplicatia
                                               ıns˘                    ¸
a primit un singur argument):
     Hello Java

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

Obiecte ¸i clase
        s

2.1     Ciclul de viat˘ al unui obiect
                     ¸a
2.1.1    Crearea obiectelor
In Java, ca ˆ orice limbaj de programare orientat-obiect, crearea obiectelor
            ın
se 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
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          ¸        a
obiecte 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
                                             a
care este responsabil cu initializarea obiectului cu valorile implicite. Dup˘
                             ¸                                                 a
cum 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                                                    a
parametrii respectivi.
    Fiecare clas˘ are un set de constructori care se ocup˘ cu initializare
                 a                                             a        ¸
obiectelor nou create. De exemplu, clasa Rectangle are urm˘torii construc-
                                                                a
tori:

     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            s
linie (cazul cel mai uzual):

     Rectangle patrat = new Rectangle(0, 0, 100, 100);

    Obiecte anonime
    Este posibil˘ ¸i crearea unor obiecte anonime care servesc doar pentru
                 a s
initializarea altor obiecte, caz ˆ care etapa de declarare a referintei obiectului
   ¸                             ın                                 ¸
nu mai este prezent˘: a

     Rectangle patrat = new Rectangle(new Point(0,0),
                                      new Dimension(100, 100));
¸˘
2.1. CICLUL DE VIATA AL UNUI OBIECT                                         37

   Spatiul de memorie nu este pre-alocat
       ¸
   Declararea unui obiect nu implic˘ sub nici o form˘ alocarea de spatiu
                                   a                a                ¸
de memorie pentru acel obiect. Alocarea memoriei se face doar la apelul
operatorului new.

  Rectangle patrat;
  patrat.x = 10;
  //Eroare - lipseste instantierea


2.1.2     Folosirea obiectelor
Odat˘ un obiect creat, el poate fi folosit ˆ urm˘toarele sensuri: aflarea unor
      a                                    ın    a
informatii despre obiect, schimbarea st˘rii sale sau executarea unor actiuni.
         ¸                               a                              ¸
Aceste lucruri se realizeaza prin aflarea sau schimbarea valorilor variabilelor
sale, 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
                                               ın
acces pe care le ofer˘ variabilele respective celorlalte clase. (vezi ”Modifica-
                     a
tori de acces pentru membrii unei clase”)
    Apelul unei metode se face prin obiect.metoda([parametri]).

  Rectangle patrat = new Rectangle(0, 0, 100, 200);
  patrat.setLocation(10, 20); //schimba originea
  patrat.setSize(200, 300); //schimba dimensiunea

    Se observ˘ c˘ valorile variabilelor pot fi modificate indirect prin inter-
              a a
mediul metodelor sale. Programarea orientat˘ obiect descurajeaz˘ folosirea
                                              a                   a
direct˘ a variabilelor unui obiect deoarece acesta poate fi adus ˆ st˘ri in-
      a                                                          ın a
consistente (ireale). In schimb, pentru fiecare variabil˘ care descrie starea
                                                       a
38                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

obiectului trebuie s˘ existe metode care s˘ permit˘ schimbarea/aflarea val-
                     a                    a        a
orilor variabilelor sale. Acestea se numesc metode de accesare, sau metode
setter - getter ¸i au numele de forma setVariabila, respectiv getVariabila.
                s

     patrat.width = -100;         //stare inconsistenta
     patrat.setSize(-100, -200); //metoda setter
     //metoda setSize poate sa testeze daca noile valori sunt
     //corecte si sa valideze sau nu schimbarea lor


2.1.3      Distrugerea obiectelor
Multe limbaje de programare impun ca programatorul s˘ ¸in˘ evidenta obiectelor
                                                        at a        ¸
create ¸i s˘ le distrug˘ ˆ mod explicit atunci cˆnd nu mai este nevoie de ele,
       s a             a ın                     a
cu alte cuvinte s˘ administreze singur memoria ocupat˘ de obiectele sale.
                   a                                      a
Practica a demonstrat c˘ aceast˘ tehnic˘ este una din principalele furnizoare
                          a      a       a
de erori ce duc la functionarea defectuoas˘ a programelor.
                        ¸                   a
    In Java programatorul nu mai are responsabilitatea distrugerii obiectelor
sale ˆ
     ıntrucˆt, ˆ momentul rul˘rii unui program, simultan cu interpretorul
            a ın                a
Java, ruleaz˘ ¸i un proces care se ocup˘ cu distrugerea obiectelor care nu
              a s                         a
mai sunt folosite. Acest proces pus la dispozitie de platforma Java de lucru
                                               ¸
se nume¸te garbage collector (colector de gunoi), prescurtat gc.
         s
    Un obiect este eliminat din memorie de procesul de colectare atunci cˆnd
                                                                          a
nu 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-
                                                               a
cut˘ 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                                                  a
eliminate automat din memorie.
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            ın
sunt 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                                           a
obiect posibilitatea ”s˘ curete dup˘ el”, apelˆnd metoda de finalizare a obiec-
                       a      ¸     a           a
tului respectiv. Uzual, ˆ timpul finaliz˘rii un obiect ˆsi inchide fisierele ¸i
                          ın                a              ı¸                 s
socket-urile folosite, distruge referintele c˘tre alte obiecte (pentru a u¸sura
                                       ¸      a                           s
sarcina colectorului de gunoaie), etc.
   Codul pentru finalizarea unui obiect trebuie scris ˆ   ıntr-o metod˘ special˘
                                                                      a       a
numita finalize a clasei ce descrie obiectul respectiv. (vezi ”Clasa Object”)


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




2.2      Crearea claselor
2.2.1     Declararea claselor
Clasele reprezint˘ o modalitate de a introduce noi tipuri de date ˆ
                  a                                                       ıntr-o
aplicatie 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                         s
sunt:
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                    a
clas˘ este subclas˘ a unei alte clase cu numele NumeSuperclasa sau/¸i c˘
    a             a                                                    s a
implementeaz˘ una sau mai multe interfete, ale c˘ror nume trebuie separate
              a                          ¸      a
prin virgul˘.
           a

2.2.2      Extinderea claselor
Spre deosebire de alte limbaje de programare orientate-obiect, Java permite
doar mo¸tenirea simpl˘, ceea ce ˆ
         s                a          ıneamn˘ c˘ o clas˘ poate avea un singur
                                           a a        a
p˘rinte (superclas˘). Evident, o clas˘ poate avea oricˆti mo¸tenitori (sub-
  a                a                   a                a     s
clase), de unde rezult˘ c˘ multimea tuturor claselor definite ˆ Java poate fi
                       a a     ¸                             ın
vazut˘ ca un arbore, r˘d˘cina acestuia fiind clasa Object. A¸adar, Object
      a                 a a                                    s
este singura clas˘ care nu are p˘rinte, fiind foarte important˘ ˆ modul de
                 a                a                           a ın
lucru cu obiecte si structuri de date ˆ Java.
                                      ın
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                    s
care nu sunt private.

2.2.3    Corpul unei clase
Corpul unei clase urmeaz˘ imediat dup˘ declararea clasei ¸i este cuprins ˆ
                        a             a                  s               ıntre
acolade. Continutul acestuia este format din:
             ¸

   • Declararea ¸i, eventual, initializarea variabilelor de instant˘ ¸i de clas˘
                s                ¸                                ¸a s         a
     (cunoscute ˆ
                ımpreun˘ ca variabile membre).
                         a

   • Declararea ¸i implementarea constructorilor.
                s

   • Declararea ¸i implementarea metodelor de instanta ¸i de clas˘ (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                          ın
clasei, urmˆnd ca implementare s˘ fie facut˘ ˆ afara ei. Implementarea
           a                        a         a ın
metodelor unei clase trebuie s˘ se fac˘ obligatoriu ˆ corpul clasei.
                              a       a             ın

// C++
class A {
  void metoda1();
  int metoda2() {
    // Implementare
  }
}
A::metoda1() {
  // Implementare
}
42                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

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

   Variabilele unei clase pot avea acela¸i nume cu metodele clasei, care poate
                                         s
fi chiar numele clasei, f˘r˘ a exista posibilitatea aparitiei vreunei ambiguit˘¸i
                        aa                              ¸                    at
din 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          at
dovedind 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                                     a




2.2.4      Constructorii unei clase
Constructorii unei clase sunt metode speciale care au acela¸i nume cu cel
                                                             s
al clasei, nu returneaz˘ nici o valoare ¸i sunt folositi pentru initializarea
                         a              s             ¸            ¸
obiectelor acelei clase ˆ momentul instantierii lor.
                        ın                ¸

class NumeClasa {
  [modificatori] NumeClasa([argumente]) {
    // Constructor
2.2. CREAREA CLASELOR                                                        43

    }
}

    O clas˘ poate avea unul sau mai multi constructori care trebuie ˆ a s˘
           a                                 ¸                           ıns˘ a
difere prin lista de argumente primite. In felul acesta sunt permise diverse
tipuri de initializ˘ri ale obiectelor la crearea lor, ˆ functie de num˘rul para-
             ¸     a                                  ın    ¸         a
metrilor 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                                                 a

class Dreptunghi {
  double x, y, w, h;
  Dreptunghi(double x1, double y1, double w1, double h1) {
    // Cel mai general constructor
    x=x1; y=y1; w=w1; h=h1;
    System.out.println("Instantiere dreptunghi");
  }
  Dreptunghi(double w1, double h1) {
    // Constructor cu doua argumente
    x=0; y=0; w=w1; h=h1;
    System.out.println("Instantiere dreptunghi");
  }

    Dreptunghi() {
      // Constructor fara argumente
      x=0; y=0; w=0; h=0;
      System.out.println("Instantiere dreptunghi");
    }
}

   Constructorii sunt apelati automat la instantierea unui obiect. In cazul
                           ¸                   ¸
ˆ care dorim s˘ apel˘m explicit constructorul unei clase folosim expresia
ın            a     a

                            this( argumente ),

care apeleaz˘ constructorul corespunz˘tor (ca argumente) al clasei respec-
             a                         a
tive. 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
44                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

a repeta acelea¸i secvente de cod ˆ toti constructorii (cum ar fi afi¸area
               s        ¸          ın ¸                                 s
mesajului ”Instantiere dreptunghi”), clasa de mai sus poate fi rescris˘ astfel:
                                                                     a

class Dreptunghi {
  double x, y, w, h;
  Dreptunghi(double x1, double y1, double w1, double h1) {
    // Implementam doar constructorul cel mai general
    x=x1; y=y1; w=w1; h=h1;
    System.out.println("Instantiere dreptunghi");
  }
  Dreptunghi(double w1, double h1) {
    this(0, 0, w1, h1);
    // Apelam constructorul cu 4 argumente
  }

     Dreptunghi() {
       this(0, 0);
       // Apelam constructorul cu 2 argumente
     }
}

     Dintr-o subclas˘ putem apela explicit constructorii superclasei cu expresia
                    a
                             super( argumente ).
     S˘ presupunem c˘ dorim s˘ cre˘m clasa Patrat, derivat˘ din clasa Dreptunghi:
      a             a        a a                          a
class Patrat extends Dreptunghi {
  Patrat(double x, double y, double d) {
    super(x, y, d, d);
    // Apelam constructorul superclasei
  }
}



    Atentie
         ¸
    Apelul explcit al unui constructor nu poate ap˘rea decˆt ˆ
                                                   a       a ıntr-un alt con-
structor si trebuie s˘ fie prima instructiune din constructorul respectiv.
                     a                 ¸
2.2. CREAREA CLASELOR                                                      45

    Constructorul implicit
    Constructorii sunt apelati automat la instantierea unui obiect. In cazul
                            ¸                   ¸
ˆ care scriem o clas˘ care nu are declarat nici un constructor, sistemul ˆ
ın                    a                                                    ıi
creeaz˘ automat un constructor implicit, care nu prime¸te nici un argument
       a                                                s
¸i care nu face nimic. Deci prezenta constructorilor ˆ corpul unei clase nu
s                                  ¸                 ın
este obligatorie. Dac˘ ˆ a scriem un constructor pentru o clas˘, care are mai
                     a ıns˘                                   a
mult de un argument, atunci constructorul implicit (f˘r˘ nici un argument)
                                                      aa
nu va mai fi furnizat implicit de c˘tre sistem. S˘ consider˘m, ca exemplu,
                                   a             a          a
urm˘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                         ¸                                 a
implic˘ instantierea unui obiect din clasa p˘rinte. Din acest motiv, fiecare
      a        ¸                              a
constructor 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               a
expresia super([argumente]), ˆ caz contrar fiind semnalat˘ o eroare la
                                  ın                            a
compilare.
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:
                                        a
public, protected, private ¸i cel implicit.
                                s

     • public
       In orice alt˘ clas˘ se pot crea instante ale clasei respective.
                   a     a                   ¸

     • protected
       Doar ˆ subclase pot 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 variabilelor
Variabilele membre ale unei clase se declar˘ de obicei ˆ
                                           a           ınaintea metodelor,
de¸i acest lucru nu este impus de c˘tre compilator.
  s                                a
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     ın
unei metode, fiind vizibile ˆ toate metodele respectivei clase. Variabilele
                            ın
declarate ˆ 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;
}
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”)
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
               s
Sunt variabile predefinite care fac referinta, ˆ cadrul unui obiect, la obiectul
                                          ¸ ın
propriu-zis (this), respectiv la instanta p˘rintelui (super). Sunt folosite
                                         ¸ a
ˆ general pentru a rezolva conflicte de nume prin referirea explicit˘ a unei
ın                                                                     a
variabile sau metode membre. Dup˘ cum am v˘zut, utilizate sub form˘ de
                                     a           a                         a
metode au rolul de a apela constructorii corespunz˘tori ca argumente ai clasei
                                                   a
curente, respectiv ai superclasei

class A {
  int x;
  A() {
    this(0);
  }
  A(int x) {
    this.x = x;
  }
  void metoda() {
    x ++;
  }
}

class B extends A {
  B() {
    this(0);
  }
  B(int x) {
    super(x);
    System.out.println(x);
  }

  void metoda() {
    super.metoda();
50                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

         System.out.println(x);
     }
}


2.3         Implementarea metodelor
2.3.1        Declararea metodelor
Metodele sunt responsabile cu descrierea comportamentului unui obiect. In-
trucˆt Java este un limbaj de programare complet orientat-obiect, metodele
    a
se 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
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                           ¸
52                                    CAPITOLUL 2. OBIECTE SI CLASE
                                                           ¸

2.3.2      Tipul returnat de o metod˘
                                    a
Metodele pot sau nu s˘ returneze o valoare la terminarea lor. Tipul returnat
                      a
poate fi atˆt un tip primitiv de date sau o referint˘ la un obiect al unei clase.
          a                                       ¸a
In cazul ˆ care o metod˘ nu returneaz˘ nimic atunci trebuie obligatoriu
         ın               a               a
specificat 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                                             a
intermediul instructiunii return, care trebuie s˘ apar˘ ˆ toate situatiile de
                   ¸                            a      a ın             ¸
terminare a functiei.
                 ¸

     double radical(double x) {
       if (x >= 0)
         return Math.sqrt(x);
       else {
         System.out.println("Argument negativ !");
         // Eroare la compilare
         // Lipseste return pe aceasta ramura
       }
     }

    In cazul ˆ care ˆ declaratia functiei tipul returnat este un tip primitiv de
             ın     ın         ¸        ¸
date, valoarea returnat˘ la terminarea functiei trebuie s˘ aib˘ obligatoriu acel
                        a                     ¸             a   a
tip sau un subtip al s˘u, altfel va fi furnizat˘ o eroare la compilare. In general,
                      a                       a
orice atribuire care implic˘ pierderi de date este tratat˘ de compilator ca
                             a                                a
eroare.

     int metoda() {
       return 1.2; // Eroare
     }

     int metoda() {
2.3. IMPLEMENTAREA METODELOR                                                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               ¸a
clasa obiectului returnat trebuie s˘ coincid˘ sau s˘ fie o subclas˘ a clasei
                                   a         a       a                a
specificate la declararea metodei. De exemplu, fie clasa Poligon ¸i subclasa
                                                                    s
acesteia Patrat.

  Poligon metoda1( ) {
    Poligon p = new Poligon();
    Patrat t = new Patrat();
    if (...)
      return p; // Corect
    else
      return t; // Corect
  }
  Patrat metoda2( ) {
    Poligon p = new Poligon();
    Patrat t = new Patrat();
    if (...)
      return p; // Eroare
    else
      return t; // Corect
  }


2.3.3     Trimiterea parametrilor c˘tre o metod˘
                                   a           a
Signatura unei metode este dat˘ de numarul ¸i tipul argumentelor primite
                                 a               s
de acea metod˘. Tipul de date al unui argument poate fi orice tip valid al
              a
limbajului Java, atˆt tip primitiv cˆt ¸i tip referint˘.
                   a                a s              ¸a

  TipReturnat metoda([Tip1 arg1, Tip2 arg2, ...])

   Exemplu:
54                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

     void adaugarePersoana(String nume, int varsta, float salariu)
     // String este tip referinta
     // int si float sunt tipuri primitive

    Spre deosebire de alte limbaje, ˆ Java nu pot fi trimise ca parametri ai
                                      ın
unei 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    ¸ a
Pˆna la aparitia versiunii 1.5, ˆ Java o metod˘ nu putea primi un num˘r
  a             ¸                 ın              a                             a
variabil de argumente, ceea ce ˆ ınseamna c˘ apelul unei metode trebuia s˘ se
                                            a                                 a
fac˘ cu specificarea exact˘ a numarului ¸i tipurilor argumentelor. Vom anal-
    a                      a             s
iza ˆıntr-o sectiune separat˘ modalitate de specificare a unui num˘r variabil
               ¸            a                                        a
de argumente pentru o metod˘.   a
Numele argumentelor primite trebuie s˘ difere ˆ
                                         a        ıntre ele ¸i nu trebuie s˘ co-
                                                            s               a
incid˘ cu numele nici uneia din variabilele locale ale metodei. Pot ˆ a s˘
      a                                                                  ıns˘ a
coincid˘ cu numele variabilelor membre ale clasei, caz ˆ care diferentierea
         a                                                 ın              ¸
dintre ele se va face prin intermediul variabile this.

class Cerc {
  int x, y, raza;
  public Cerc(int x, int y, int raza) {
    this.x = x;
    this.y = y;
    this.raza = raza;
  }
}

    In Java argumentele sunt trimise doar prin valoare (pass-by-value).
Acest lucru ˆınseamn˘ c˘ metoda receptioneaz˘ doar valorile variabilelor prim-
                      a a               ¸     a
ite ca parametri.
Cˆnd argumentul are tip primitiv de date, metoda nu-i poate schimba val-
  a
oarea decˆt local (ˆ cadrul metodei); la revenirea din metod˘ variabila are
           a         ın                                          a
aceea¸i valoare ca ˆ
      s             ınaintea apelului, modific˘rile f˘cute ˆ cadrul metodei fi-
                                             a      a     ın
ind pierdute.
Cˆnd argumentul este de tip referint˘, metoda nu poate schimba valoarea
  a                                    ¸a
referintei obiectului, ˆ a poate apela metodele acelui obiect ¸i poate modifica
        ¸              ıns˘                                   s
orice variabil˘ membr˘ accesibil˘.
              a         a         a
    A¸adar, dac˘ dorim ca o metod˘ s˘ schimbe starea (valoarea) unui argu-
      s          a                   a a
ment primit, atunci el trebuie s˘ fie neaparat de tip referint˘.
                                 a                           ¸a
2.3. IMPLEMENTAREA METODELOR                                              55

De exemplu, s˘ consider˘m clasa Cerc descris˘ anterior ˆ care dorim s˘
             a         a                    a           ın           a
implement˘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           s
valorile 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        a
nu realizeaz˘ nimic pentru c˘ nu poate schimba valorile variabilelor primite
             a               a
ca argumente.
   Pentru a rezolva lucrul propus trebuie s˘ definim o clas˘ suplimentar˘
                                             a               a              a
care s˘ descrie parametrii pe care dorim s˘-i afl˘m:
      a                                   a     a

// Varianta corecta
class Param {
  public int x, y, raza;
}

class Cerc {
  private int x, y, raza;
  public void aflaParametri(Param param) {
    param.x = x;
    param.y = y;
    param.raza = raza;
  }
}

   Argumentul param are tip referint˘ ¸i, de¸i nu ˆ schimb˘m valoarea (val-
                                    ¸a s    s     ıi       a
oarea sa este adresa de memorie la care se gase¸te ¸i nu poate fi schimbat˘),
                                               s s                       a
56                                 CAPITOLUL 2. OBIECTE SI CLASE
                                                        ¸

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


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

2.3.4      Metode cu num˘r variabil de argumente
                        a
Incepˆnd cu versiunea 1.5 a limbajului Java, exist˘ posibilitate de a declara
     a                                              a
metode 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       ¸              a
variabil de argumente, ˆ functie de apelul metodei. Tipul argumentelor
                          ın    ¸
poate fi referint˘ sau primitiv. Metoda de mai jos afi¸eaz˘ argumentele prim-
               ¸a                                   s a
ite, 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);
2.3. IMPLEMENTAREA METODELOR                                                57

2.3.5    Supraˆ arcarea ¸i supradefinirea metodelor
              ınc˘      s
Supraˆ arcarea ¸i supradefinirea metodelor sunt dou˘ concepte extrem de
       ınc˘       s                                      a
utile ale program˘rii orientate obiect, cunoscute ¸i sub denumirea de polimor-
                  a                               s
fism, ¸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.
              a
class A {
  void metoda() {
    System.out.println("A: metoda fara parametru");
  }
  // Supraincarcare
  void metoda(int arg) {
    System.out.println("A: metoda cu un parametru");
  }
}

class B extends A {
  // Supradefinire
  void metoda() {
    System.out.println("B: metoda fara parametru");
  }
}
   O metod˘ 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"
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               a
metodele abstracte ale superclasei (dac˘ este cazul). In cazul ˆ care o clas˘
                                         a                      ın          a
nu supradefine¸te toate metodele abstracte ale p˘rintelui, ea ˆ a¸i este ab-
                 s                                a             ıns˘s
stract˘ ¸i va trebui declarat˘ ca atare.
      as                     a
    In Java nu este posibil˘ supraˆ arcarea operatorilor.
                           a       ınc˘


2.4      Modificatori de acces
Modificatorii de acces sunt cuvinte rezervate ce controleaz˘ accesul celor-
                                                               a
late clase la membrii unei clase. Specificatorii de acces pentru variabilele
¸i metodele unei clase sunt: public, protected, private ¸i cel implicit (la
s                                                            s
nivel de pachet), iar nivelul lor de acces este dat ˆ tabelul de mai jos:
                                                    ın

              Specificator Clasa Sublasa Pachet Oriunde
              private      X
              protected    X      X*      X
              public       X      X       X       X
              implicit     X              X
¸˘ ¸                ˘
2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA                                   59

    A¸adar, dac˘ nu este specificat nici un modificator de acces, implicit
      s         a
nivelul de acces este la nivelul pachetului. In cazul ˆ care declar˘m un
                                                        ın            a
membru ”protected” atunci accesul la acel membru este permis din subclasele
clasei ˆ care a fost declarat dar depinde ¸i de pachetul ˆ care se gase¸te
        ın                                  s              ın             s
subclasa: dac˘ sunt ˆ acela¸i pachet accesul este permis, dac˘ nu sunt ˆ
              a       ın      s                                  a          ın
acela¸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                a
O 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.
            a

2.5.1     Variabile de instant˘ ¸i de clas˘
                             ¸a s         a
Cˆnd declar˘m o variabil˘ membr˘ f˘r˘ modificatorul static, cum ar fi x ˆ
  a        a            a      a aa                                   ın
exemplul de mai jos:

class Exemplu {
  int x ; //variabila de instanta
}

se declar˘ de fapt o variabil˘ de instant˘, ceea ce ˆ
         a                   a          ¸a          ınseamn˘ c˘ la fiecare creare
                                                           a a
a unui obiect al clasei Exemplu sistemul aloc˘ o zon˘ de memorie separat˘
                                                 a       a                     a
pentru memorarea valorii lui x.

  Exemplu o1 = new Exemplu();
  o1.x = 100;
60                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

     Exemplu o2 = new Exemplu();
     o2.x = 200;
     System.out.println(o1.x); // Afiseaza 100
     System.out.println(o2.x); // Afiseaza 200

    A¸adar, fiecare obiect nou creat va putea memora valori diferite pentru
      s
variabilele sale de instant˘.
                           ¸a
    Pentru variabilele de clas˘ (statice) sistemul aloc˘ o singur˘ zon˘ de mem-
                               a                        a        a    a
orie la care au acces toate instantele clasei respective, ceea ce ˆ
                                    ¸                               ınseamn˘ c˘
                                                                            a a
dac˘ un obiect modific˘ valoarea unei variabile statice ea se va modifica ¸i
    a                    a                                                     s
pentru toate celelalte obiecte. Deoarece nu depind de o anumit˘ instanta a
                                                                    a       ¸˘
unei clase, variabilele statice pot fi referite ¸i sub forma:
                                               s

                    NumeClasa.numeVariabilaStatica

class 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˘    ın
memorie a clasei respective, ¸i este realizat˘ prin atribuiri obi¸nuite:
                             s               a                   s

class Exemplu {
  static final double PI = 3.14;
  static long nrInstante = 0;
  static Point p = new Point(0,0);
}
¸˘ ¸                ˘
2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA                                 61

2.5.2    Metode de instant˘ ¸i de clas˘
                         ¸a s         a
Similar ca la variabile, metodele declarate f˘r˘ modificatorul static sunt
                                             aa
metode de instant˘ iar cele declarate cu static sunt metode de clas˘ (stat-
                  ¸a                                               a
ice). 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                   a
de starea obiectelor clasei respective, apelul lor se poate face ¸i sub forma:
                                                                 s

                      NumeClasa.numeMetodaStatica

  Exemplu.metodaStatica();     // Corect, echivalent cu
  Exemplu obj = new Exemplu();
  obj.metodaStatica();         // Corect, de asemenea

   Metodele de instant˘ nu pot fi apelate decˆt pentru un obiect al clasei
                     ¸a                     a
respective:

  Exemplu.metodaDeInstanta(); // Eroare la compilare !
  Exemplu obj = new Exemplu();
  obj.metodaDeInstanta();      // Corect
62                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

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


     Declararea eficient˘ a constantelor
                          a
     S˘ consider˘m situatia cˆnd dorim s˘ declar˘m o constant˘.
      a         a       ¸ a             a       a            a

class Exemplu {
  final double PI = 3.14;
  // Variabila finala de instanta
}

   La fiecare instantiere a clasei Exemplu va fi rezervat˘ o zon˘ de memorie
                    ¸                                      a       a
pentru variabilele finale ale obiectului respectiv, ceea ce este o risip˘ ˆ
                                                                       a ıntrucˆt
                                                                               a
aceste 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                           s
pentru a le rezerva o singur˘ zon˘ de memorie, comun˘ tuturor obiectelor:
                             a    a                      a

class Exemplu {
  static final double PI = 3.14;
  // Variabila finala de clasa
}



   Num˘rarea obiectelor unei clase
          a
   Num˘rarea obiectelor unei clase poate fi f˘cut˘ extrem de simplu folosind
        a                                        a a
o variabil˘ static˘ ¸i este util˘ ˆ situatiile cˆnd trebuie s˘ control˘m diver¸i
          a       as            a ın     ¸      a            a        a       s
parametri legati de crearea obiectelor unei clase.
               ¸

class Exemplu {
  static long nrInstante = 0;
  Exemplu() {
    // Constructorul este apelat la fiecare instantiere
    nrInstante ++;
  }
}
¸˘ ¸                ˘
2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA                                   63

    Implementarea functiilor globale
                           ¸
    Spre deosebire de limbajele de programare procedurale, ˆ Java nu putem
                                                             ın
avea functii globale definite ca atare, ˆ
          ¸                              ıntrucˆt ”orice este un obiect”. Din
                                               a
acest motiv chiar ¸i metodele care au o functionalitate global˘ trebuie im-
                   s                            ¸                 a
plementate ˆ cadrul unor clase. Acest lucru se va face prin intermediul
            ın
metodelor de clas˘ (globale), deoarece acestea nu depind de starea partic-
                   a
ular˘ 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                     a
nu ar fi fost functie de clas˘, apelul ei ar fi trebuit f˘cut astfel (incorect, de
                 ¸          a                          a
altfel):
  // 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      a
apelat˘ prin: Math.sqrt(121) .
      a
A¸adar, functiile globale necesare unei aplicatii vor fi grupate corespunz˘tor
  s          ¸                                ¸                          a
ˆ diverse clase ¸i implementate ca metode statice.
ın               s

2.5.4     Blocuri statice de initializare
                                ¸
Variabilele statice ale unei clase sunt initializate la un moment care precede
                                           ¸
prima utilizare activ˘ a clasei respective. Momentul efectiv depinde de im-
                      a
plementarea 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               s
blocurile statice de initializare ale clasei. Acestea sunt secvente de cod de
                        ¸                                         ¸
forma:
  static {
    // Bloc static de initializare;
    ...
  }
care se comport˘ ca o metod˘ static˘ apelat˘ automat de c˘tre ma¸ina vir-
                a             a       a      a                a      s
tual˘. Variabilele referite ˆ
    a                       ıntr-un bloc static de initializare trebuie s˘ fie
                                                      ¸                  a
obligatoriu de clas˘ sau locale blocului:
                   a
public class Test {
  // Declaratii de variabile statice
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 imbricate
2.6.1      Definirea claselor imbricate
O 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 se
s      a                        ¸            ¸
poate face fie ca membru al clasei de acoperire - caz ˆ care este accesibil˘
                                                        ın                    a
tuturor metodelor, fie local ˆ cadrul unei metode.
                             ın

class ClasaDeAcoperire{
  class ClasaImbricata1 {
    // Clasa membru
  }
  void metoda() {
    class ClasaImbricata2 {
      // Clasa locala metodei
    }
  }
2.6. CLASE IMBRICATE                                                         65

}

   Folosirea claselor imbricate se face atunci cˆnd o clas˘ are nevoie ˆ im-
                                                  a         a              ın
plementarea ei de o alt˘ clas˘ ¸i nu exist˘ nici un motiv pentru care aceasta
                        a    as            a
din 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                  s
acces nerestrictionat la toate variabilele clasei de acoperire, chiar dac˘ aces-
               ¸                                                          a
tea sunt private. O clas˘ declarat˘ local˘ unei metode va avea acces ¸i la
                          a         a       a                                s
variabilele finale declarate ˆ metoda respectiv˘.
                            ın                    a

class ClasaDeAcoperire{
  private int x=1;

    class ClasaImbricata1 {
      int a=x;
    }
    void metoda() {
      final int y=2;
      int z=3;
      class ClasaImbricata2 {
        int b=x;
        int c=y;

            int d=z; // Incorect
        }
    }
}

    O clas˘ imbricat˘ membr˘ (care nu este local˘ unei metode) poate fi
           a          a         a                     a
referit˘ din exteriorul clasei de acoperire folosind expresia
       a

                      ClasaDeAcoperire.ClasaImbricata

A¸adar, clasele membru pot fi declarate cu modificatorii public, protected,
  s
private pentru a controla nivelul lor de acces din exterior, ˆ
                                                             ıntocmai ca orice
variabil˘ sau metod˘ mebr˘ a clasei. Pentru clasele imbricate locale unei
        a            a       a
metode nu sunt permi¸i acesti modificatori.
                       s      ¸
    Toate clasele imbricate pot fi declarate folosind modificatorii abstract ¸i
                                                                            s
final, semnificatia lor fiind aceea¸i ca ¸i ˆ cazul claselor obi¸nuite.
                  ¸                s    s ın                   s
66                                    CAPITOLUL 2. OBIECTE SI CLASE
                                                           ¸

2.6.2      Clase interne
Spre deosebire de clasele obi¸nuite, o clas˘ imbricat˘ poate fi declarat˘ static˘
                             s             a         a                 a       a
sau nu. O clas˘ imbricat˘ nestatic˘ se nume¸te clasa intern˘.
               a          a          a         s               a
class 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                 ¸                        s
la toti membrii clasei sale de acoperire.
     ¸

2.6.3      Identificare claselor imbricate
Dup˘ cum ¸tim orice clas˘ produce la compilare a¸a numitele ”unit˘¸i de com-
     a     s             a                        s               at
pilare”, 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          ¸                ¸                           a
bricate aceste unit˘¸i de compilare sunt denumite astfel: numele clasei de
                   at
acoperire, urmat de simbolul ’$’ apoi de numele clasei imbricate.
class ClasaDeAcoperire{
  class ClasaInterna1 {}
  class ClasaInterna2 {}
}
2.7. CLASE SI METODE ABSTRACTE
           ¸                                                                  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    a
unui ’$’ ¸i apoi numele clasei imbricate.
         s

2.6.4     Clase anonime
Exist˘ posibilitatea definirii unor clase imbricate locale, f˘r˘ nume, utilizate
     a                                                      aa
doar pentru instantierea unui obiect de un anumit tip. Astfel de clase se
                     ¸
numesc clase anonime ¸i sunt foarte utile ˆ situatii cum ar fi crearea unor
                         s                  ın      ¸
obiecte ce implementeaz˘ o anumit˘ interfat˘ sau extind o anumit˘ clas˘
                           a          a        ¸a                      a      a
abstract˘.
         a
    Exemple de folosire a claselor anonime vor 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              a
de forma ClasaAcoperire.$1,..., ClasaAcoperire.$n, unde n este num˘rul     a
de clase anonime definite ˆ clasa respectiv˘ de acoperire.
                            ın              a


2.7      Clase ¸i metode abstracte
               s
Uneori ˆ proiectarea unei aplicatii este necesar s˘ reprezent˘m cu ajutorul
        ın                        ¸                  a             a
claselor concepte abstracte care s˘ nu poat˘ fi instantiate ¸i care s˘ foloseasc˘
                                  a         a           ¸     s       a         a
doar la dezvoltarea ulterioar˘ a unor clase ce descriu obiecte concrete. De ex-
                              a
emplu, ˆ pachetul java.lang exist˘ clasa abstract˘ Number care modeleaz˘
        ın                           a                 a                        a
conceptul 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 Number
serve¸te ca superclas˘ pentru clasele concrete Byte, Double, Float, Integer,
     s               a
Long ¸i Short, ce implementeaz˘ obiecte pentru descrierea numerelor de un
      s                          a
anumit tip. A¸adar, clasa Number reprezint˘ un concept abstract ¸i nu vom
               s                              a                         s
putea instantia obiecte de acest tip - vom folosi ˆ schimb subclasele sale.
             ¸                                      ın

  Number numar = new Number();      // Eroare
  Integer intreg = new Integer(10); // Corect
68                                  CAPITOLUL 2. OBIECTE SI CLASE
                                                         ¸

2.7.1      Declararea unei clase abstracte


Declararea unei clase abstracte se face folosind 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         a
la nivel de pachet, dar nu poate specifica modificatorul final, combinatia   ¸
abstract final fiind semnalat˘ ca eroare la compilare - de altfel, o clas˘
                                 a                                           a
declarat˘ astfel nu ar avea nici o utilitate.
         a
    O clas˘ abstract˘ poate contine acelea¸i elemente membre ca o clas˘
           a          a           ¸           s                              a
obi¸nuit˘, la care se adaug˘ declaratii de metode abstracte - f˘r˘ nici o im-
   s     a                 a         ¸                         aa
plementare.

2.7.2      Metode abstracte
Spre deosebire de clasele obi¸nuite care trebuie s˘ furnizeze implement˘ri
                               s                    a                     a
pentru toate metodele declarate, o clas˘ abstract˘ poate contine metode f˘r˘
                                       a          a          ¸            aa
nici o implementare. Metodele fara nici o implementare se numesc metode
abstracte ¸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                                        a
eroare de compilare.


abstract class ClasaAbstracta {
  abstract void metodaAbstracta(); // Corect
  void metoda();                   // Eroare
}

   In felul acesta, o clas˘ abstract˘ poate pune la dispozitia subclaselor sale
                          a         a                      ¸
un model complet pe care trebuie s˘-l implementeze, furnizˆnd chiar imple-
                                      a                       a
mentarea unor metode comune tuturor claselor ¸i l˘sˆnd explicitarea altora
                                                  s aa
2.7. CLASE SI METODE ABSTRACTE
           ¸                                                              69

fiec˘rei subclase ˆ parte.
    a             ın
Un exemplu elocvent de folosire a claselor ¸i metodelor abstracte este de-
                                              s
scrierea 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                                         ın
tajul nostru putem declara o clas˘ generic˘ GraphicObject care s˘ fie su-
                                     a       a                        a
perclas˘ pentru celelalte clase. Metodele abstracte vor fi folosite pentru im-
        a
plementarea comportamentului specific fiec˘rui obiect, cum ar fi desenarea
                                             a
iar cele obi¸nuite pentru comportamentul comun tuturor, cum ar fi schim-
            s
barea originii. Implementarea clasei abstracte GraphicObject ar putea ar˘ta
                                                                          a
astfel:

abstract class GraphicObject {
  // Stari comune
  private int x, y;
  private Color color = Color.black;
  ...

    // Metode comune
    public void setX(int x) {
      this.x = x;
    }
    public void setY(int y) {
      this.y = y;
    }
    public void setColor(Color color) {
      this.color = color;
    }
    ...

    // Metode abstracte
    abstract void draw();
    ...
}
70                                   CAPITOLUL 2. OBIECTE SI CLASE
                                                          ¸

    O subclas˘ care nu este abstract˘ a unei clase abstracte trebuie s˘ furnizeze
             a                      a                                 a
obligatoriu implement˘ri ale metodelor abstracte definite ˆ superclas˘. Im-
                       a                                     ın           a
plementarea 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 de
ierarhii care folosesc la nivelele superioare clase abstracte. Dintre cele mai
importante amintim:
     • Number: superclasa abstract˘ a tipurilor referint˘ numerice
                                  a                    ¸a
     • Reader, Writer: superclasele abstracte ale 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
                                         ¸
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 Object
2.8.1       Orice clas˘ are o superclas˘
                      a                a
Dup˘ cum am v˘zut ˆ sectiunea dedicat˘ modalit˘¸ii de creare a unei clase,
     a            a     ın     ¸           a        at
clauza ”extends” specific˘ faptul c˘ acea clas˘ extinde (mo¸tene¸te) o alt˘
                             a       a          a              s     s         a
clas˘, numit˘ superclas˘. O clas˘ poate avea o singur˘ superclas˘ (Java nu
    a          a           a       a                     a           a
suport˘ mo¸tenirea multipl˘) ¸i chiar dac˘ nu specific˘m clauza ”extends” la
       a     s                 a s         a           a
crearea unei clase ea totu¸i va avea o superclas˘. Cu alte cuvinte, ˆ Java
                              s                   a                      ın
orice clas˘ are o superclas˘ ¸i numai una. Evident, trebuie s˘ existe o exceptie
          a                  as                              a               ¸
de la aceast˘ regul˘ ¸i anume clasa care reprezint˘ r˘d˘cina ierarhiei format˘
             a       as                           a a a                        a
de 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                             a
anumit˘ superclas˘. Declaratiile de mai jos sunt echivalente:
        a            a           ¸

class Exemplu {}
class Exemplu extends Object {}


2.8.2       Clasa Object
Clasa Object este cea mai general˘ dintre clase, orice obiect fiind, direct
                                   a
sau indirect, descendent al acestei clase. Fiind p˘rintele tuturor, Object
                                                  a
define¸te ¸i implementeaz˘ comportamentul comun al tuturor celorlalte clase
      s s               a
Java, 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         ¸
72                                     CAPITOLUL 2. OBIECTE SI CLASE
                                                            ¸

   Fiind subclas˘ a lui Object, orice clas˘ ˆ poate supradefini metodele
                a                         a ıi
care 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());
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 ın
De asemenea, vom scrie un mic program TestComplex ˆ care vom testa
                                                    ın
metodele clasei definite.

                      Listing 2.1: Clasa numerelor complexe
class Complex {
  private double a ; // partea reala
  private double b ; // partea imaginara

    public Complex ( double a , double b ) {
      this . a = a ;
      this . b = b ;
    }

    public Complex () {
      this (1 , 0) ;
    }

    public boolean equals ( Object obj ) {
      if ( obj == null ) return false ;
      if (!( obj instanceof Complex ) ) return false ;

        Complex comp = ( Complex ) obj ;
        return ( comp . a == a && comp . b == b ) ;
    }

    public Object clone () {
      return new Complex (a , b ) ;
    }

    public String toString () {
      String semn = ( b > 0 ? " + " : " -" ) ;
      return a + semn + b + " i " ;
    }

    public Complex aduna ( Complex comp ) {
      Complex suma = new Complex (0 , 0) ;
      suma . a = this . a + comp . a ;
      suma . b = this . b + comp . b ;
      return suma ;
    }
}
74                                     CAPITOLUL 2. OBIECTE SI CLASE
                                                            ¸

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




2.9       Conversii automate ˆ
                             ıntre tipuri
Dup˘ cum v˘zut tipurile Java de date pot fi ˆ artie ˆ primitive ¸i referint˘.
     a      a                               ımp˘ ¸ ın            s       ¸a
Pentru fiecare tip primitiv exist˘ o clas˘ corespunz˘toare care permie lucrul
                                 a      a          a
orientat 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                  as
versia 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                                                             ıntre
tipuri primitve ¸i referint˘ sunt posibile, acest mecanism purtˆnd numele de
                s         ¸a                                   a
autoboxing, respectiv auto-unboxing. Conversia explicit˘ va fi facut˘ de c˘tre
                                                         a         a     a
compilator.
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 enumerare
Incepˆnd cu versiunea 1.5 a limbajului Java, exist˘ posibilitatea de a defini
      a                                           a
tipuri de date enumerare prin folosirea cuvˆntului cheie enum. Acest˘
                                             a                             a
solutie simplific˘ manevrarea grupurilor de constante, dup˘ cum reiese din
    ¸           a                                           a
urm˘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
76   CAPITOLUL 2. OBIECTE SI CLASE
                          ¸
Capitolul 3

Exceptii
     ¸

3.1      Ce sunt exceptiile ?
                      ¸
Termenul exceptie este o prescurtare pentru ”eveniment exceptional” ¸i poate
                 ¸                                             ¸        s
fi definit ca un eveniment ce se produce ˆ timpul executiei unui program ¸i
                                          ın              ¸                   s
care provoac˘ ˆ
              a ıntreruperea cursului normal al executiei acestuia.
                                                      ¸
    Exceptiile pot ap˘rea din diverse cauze ¸i pot avea nivele diferite de grav-
           ¸          a                      s
itate: 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 afara
t
spatiului 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            s
genul:

                                      77
78                                              CAPITOLUL 3. EXCEPTII
                                                                  ¸

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

    Crearea unui obiect de tip exceptie se nume¸te aruncarea unei exceptii
                                      ¸           s                           ¸
(”throwing an exception”). In momentul ˆ care o metod˘ genereaz˘ (arunc˘)
                                          ın             a          a         a
o exceptie sistemul de executie este responsabil cu 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
                                                                        a
metoda care a generat exceptia ¸i mergˆnd ˆ
                              ¸ s         a ınapoi pe linia apelurilor c˘trea
acea 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                  ¸                                            s
ei 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                   s
analizor pentru o anumit˘ exceptie, atunci programul Java se opre¸te cu un
                         a       ¸                                   s
mesaj de eroare (ˆ cazul exemplului de mai sus mesajul ”Aici nu se mai
                  ın
ajunge...” nu va fi afi¸at).
                      s


    Atentie
         ¸
    In Java tratarea erorilor nu mai este o optiune ci o constrˆngere. In
                                                 ¸                a
aproape toate situatile, o secvent˘ de cod care poate provoca exceptii trebuie
                   ¸             ¸a                                 ¸
s˘ specifice modalitatea de tratare a acestora.
 a




3.2       ”Prinderea” ¸i tratarea exceptiilor
                      s                ¸
Tratarea exceptiilor se realizeaz˘ prin intermediul blocurilor de instructiuni
               ¸                 a                                       ¸
try, catch ¸i finally. O secvent˘ de cod care trateaz˘ anumite exceptii
             s                      ¸a                    a                 ¸
trebuie s˘ arate astfel:
         a

     try {
       // Instructiuni care pot genera exceptii
     }
     catch (TipExceptie1 variabila) {
       // Tratarea exceptiilor de tipul 1
3.2. ”PRINDEREA” SI TRATAREA EXCEPTIILOR
                 ¸                ¸                                         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                 s
afisarea lui pe ecran. F˘r˘ a folosi tratarea exceptiilor metoda responsabil˘
                         aa                       ¸                        a
cu 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                                                 ın
tratarea erorilor este obligatorie. Folosind mecanismul exceptiilor metoda
                                                                 ¸
citeste ˆsi poate trata singur˘ erorile care pot surveni pe parcursul executiei
          ı¸                  a                                            ¸
sale. Mai jos este codul complte ¸i corect al unui program ce afi¸eaz˘ pe ecran
                                 s                              s a
continutul unui fi¸ier al c˘rui nume este primit ca argument de la linia de
    ¸              s       a
comand˘. Tratarea exceptiilor este realizat˘ complet chiar de c˘tre metoda
        a                  ¸                  a                    a
citeste.
80                                                  CAPITOLUL 3. EXCEPTII
                                                                      ¸

                       Listing 3.1: Citirea unui fisier - corect
import java . io .*;

public class CitireFisier {
  public static void citesteFisier ( String fis ) {
    FileReader f = null ;
    try {
      // Deschidem fisierul
      System . out . println ( " Deschidem fisierul " + fis ) ;
      f = new FileReader ( fis ) ;

           // Citim si afisam fisierul caracter cu caracter
           int c ;
           while ( ( c = f . read () ) != -1)
             System . out . print (( char ) c ) ;

         } catch ( File Not F o u n d E x c e p t i o n e ) {
           // Tratam un tip de exceptie
           System . err . println ( " Fisierul nu a fost gasit ! " ) ;
           System . err . println ( " Exceptie : " + e . getMessage () ) ;
           System . exit (1) ;

         } catch ( IOException e ) {
           // Tratam alt tip de exceptie
           System . out . println ( " Eroare la citirea din fisier ! " ) ;
           e . printStackTrace () ;

         } finally {
           if ( f != null ) {
             // Inchidem fisierul
             System . out . println ( "  nInchidem fisierul . " ) ;
             try {
                  f . close () ;
             } catch ( IOException e ) {
                System . err . println ( " Fisierul nu poate fi inchis ! " ) ;
                e . printStackTrace () ;
             }
           }
         }
     }

     public static void main ( String args []) {
       if ( args . length > 0)
         citesteFisier ( args [0]) ;
       else
3.2. ”PRINDEREA” SI TRATAREA EXCEPTIILOR
                 ¸                ¸                                         81

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


    Blocul ”try” contine instructiunile de deschidere a unui fi¸ier ¸i de citire
                                 ¸                            s s
dintr-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                 a
tru fiecare tip de exceptie. Inchiderea fi¸ierului se face ˆ blocul ”finally”,
                         ¸                  s              ın
deoarece 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        s
fost 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
                                                             s
la rˆndul s˘u exceptii, de exemplu atunci cˆnd fi¸ierul mai este folosit ¸i de
    a      a        ¸                         a     s                    s
alt proces ¸i nu poate fi ˆ
           s              ınchis. Deci, pentru a avea un cod complet corect
trebuie 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
                                   ¸                   a
sau mai multe blocuri ”catch”, ˆ functie de exceptiile provocate de acele
                                 ın     ¸          ¸
instructiuni sau (optional) de un bloc ”finally”.
       ¸            ¸
82                                                   CAPITOLUL 3. EXCEPTII
                                                                       ¸

3.3          ”Aruncarea” exceptiilor
                              ¸
In cazul ˆ care o metod˘ nu ˆsi asum˘ responsabilitatea trat˘rii uneia sau
         ın              a    ı¸      a                       a
mai 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                       ¸ a
leaz˘, 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                            ¸     ın
metodei citeste atunci metoda apelant˘ (main) va trebui s˘ fac˘ acest lucru:
                                     a                   a a

                             Listing 3.2: Citirea unui fisier
import java . io .*;

public class CitireFisier {
  public static void citesteFisier ( String fis )
      throws FileNotFoundException , IOException {
    FileReader f = null ;
    f = new FileReader ( fis ) ;

         int c ;
         while ( ( c = f . read () ) != -1)
           System . out . print (( char ) c ) ;

         f . close () ;
     }
3.3. ”ARUNCAREA” EXCEPTIILOR
                      ¸                                                       83


    public static void main ( String args []) {
      if ( args . length > 0) {
        try {
           citesteFisier ( args [0]) ;

          } catch ( File NotFo undE x c ep ti on e ) {
            System . err . println ( " Fisierul nu a fost gasit ! " ) ;
            System . err . println ( " Exceptie : " + e ) ;

          } catch ( IOException e ) {
            System . out . println ( " Eroare la citirea din fisier ! " ) ;
            e . printStackTrace () ;
          }

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


    Observati c˘, ˆ acest caz, nu mai putem diferentia exceptiile provocate de
             ¸ a ın                                   ¸        ¸
citirea din fi¸ier s de inchiderea fi¸ierului, ambele fiind de tipul IOException.
             s                      s
De asemenea, inchiderea fi¸ierului nu va mai fi facut˘ ˆ situatia ˆ care
                              s                           a ın          ın
apare o exceptie la citirea din fi¸ier. Este situatia ˆ care putem folosi blocul
               ¸                  s              ¸ ın
finally 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();
      }
    }
84                                                 CAPITOLUL 3. EXCEPTII
                                                                     ¸

    Metoda apelant˘ poate arunca la rˆndul sˆu exceptiile mai departe c˘tre
                    a                  a       a        ¸                a
metoda care a apelat-o la rˆndul ei. Aceast˘ ˆ antuire se termin˘ cu metoda
                            a               a ınl˘ ¸             a
main care, dac˘ va arunca exceptiile ce pot ap˘rea ˆ corpul ei, va determina
              a                  ¸             a     ın
trimiterea 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                                            s
afi¸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 clar
s                 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();
     ...
˘
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
                                       ¸                  a
fat˘ de mecanismul traditional de tratare a erorilor:
  ¸a                    ¸

    • Separarea codului pentru tratarea unei erori de codul ˆ care ea poate
                                                            ın
      s˘ apar˘.
       a     a

    • Propagarea unei erori pˆn˘ la un analizor de exceptii corespunz˘tor.
                             a a                        ¸            a

    • Gruparea erorilor dup˘ tipul lor.
                           a

3.4.1     Separarea codului pentru tratarea erorilor
In programarea traditional˘ tratarea erorilor se combin˘ cu codul ce poate
                      ¸    a                           a
produce aparitia lor producˆnd a¸a numitul ”cod spaghetti”. S˘ consider˘m
             ¸             a    s                             a        a
urm˘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                   ¸                 a
genul: ”Ce se ˆ ampl˘ dac˘: ... ?”
              ıntˆ  a    a

    • fi¸ierul nu poate fi deschis
       s

    • nu se poate determina dimensiunea fi¸ierului
                                         s
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˘
                                                                           a
extrem de mult ˆ ¸telegerea sa. In Java, folosind mecansimul exceptiilor,
                  ınt                                                  ¸
codul ar arata, schematizat, astfel:
˘
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˘.
                                      a

3.4.2    Propagarea erorilor
Propagarea unei erori se face pˆn˘ la un analizor de exceptii corespunz˘tor.
                               a a                        ¸            a
S˘ presupunem c˘ apelul la metoda citesteFisier este consecinta unor
 a                a                                                 ¸
apeluri imbricate de metode:
  int metoda1() {
    metoda2();
    ...
  }
  int metoda2() {
    metoda3;
    ...
  }
  int metoda3 {
    citesteFisier();
    ...
88                                            CAPITOLUL 3. EXCEPTII
                                                                ¸

     }

   S˘ presupunem de asemenea c˘ dorim s˘ facem tratarea erorilor doar
    a                            a         a
ˆ metoda1. Traditional, acest lucru ar trebui f˘cut prin propagarea erorii
ın               ¸                             a
produse 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    ¸                   a
sau 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;
       }
       ...
     }
˘
3.4. AVANTAJELE TRATARII EXCEPTIILOR
                              ¸                                               89

  int metoda2() throws TipExceptie {
    metoda3();
    ...
  }
  int metoda3() throws TipExceptie {
    citesteFisier();
    ...
  }

3.4.3     Gruparea erorilor dup˘ tipul lor
                               a
In Java exist˘ clase corespunz˘toare tuturor exceptiilor care pot ap˘rea la
               a                 a                     ¸                 a
executia unui program. Acestea sunt grupate ˆ functie de similarit˘¸ile
        ¸                                           ın      ¸                at
lor ˆıntr-o ierarhie de clase. De exemplu, clasa IOException se ocup˘ cu     a
exceptiile ce pot ap˘rea la operatii de intrare/iesire ¸i diferentiaz˘ la rˆndul
        ¸            a             ¸                    s        ¸ a       a
ei 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
                                                                             a
exceptii ¸i anume clasa Exception.
        ¸ s
Radacin˘ acestei ierarhii este clasa Throwable (vezi ”Ierarhia claselor ce de-
           a
scriu 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        a
tratarea 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
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
                                                   s
sunt 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                                         a
gram Java ¸i sunt invizibile programatorilor. Un program Java nu trebuie s˘
            s                                                                a
trateze aparitia acestor erori ¸i este improbabil ca o metod˘ Java s˘ provoace
              ¸                s                            a        a
asemenea erori.
3.6. EXCEPTII LA EXECUTIE
          ¸           ¸                                                       91

    Exceptiile, obiectele de tip Exception, sunt exceptiile standard (soft) care
           ¸                                          ¸
trebuie tratate de c˘tre programele Java. Dup˘ cum am mai zis tratarea aces-
                    a                           a
tor exceptii nu este o optiune ci o constrˆngere. Exceptiile care pot ”sc˘pa”
           ¸              ¸               a              ¸                 a
netratate 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
                          ¸                       ın
sustrag ˆ 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                            a
situatie extern˘, cum ar fi lipsa unui fi¸ier.
     ¸          a                         s
Aceste exceptii au o superclas˘ comun˘ RuntimeException ¸i ˆ acesata
              ¸                   a         a                      s ın
categorie sunt incluse exceptiile provocate de:
                              ¸
   • operatii aritmetice ilegale (ˆ artirea ˆ
          ¸                       ımpˆ ¸    ıntregilor la zero);
     ArithmeticException
   • accesarea membrilor unui obiect ce are valoarea null;
     NullPointerException
   • accesarea eronat˘ a elementelor unui vector.
                     a
     ArrayIndexOutOfBoundsException
    Exceptiile la executie pot ap˘rea uriunde ˆ program ¸i pot fi extrem
          ¸             ¸         a              ın           s
de numeroare iar ˆ ıncercarea de ”prindere” a lor ar fi extrem de anevoioas˘.
                                                                           a
Din acest motiv, compilatorul permite ca aceste exceptii s˘ r˘mˆn˘ netratate,
                                                      ¸ a a a a
tratarea lor nefiind ˆ a ilegal˘. Reamintim ˆ a c˘, ˆ cazul aparitiei oric˘rui
                    ıns˘      a              ıns˘ a ın           ¸       a
tip de exceptie care nu are un analizor corespunz˘tor, programul va fi termi-
             ¸                                     a
nat.
92                                               CAPITOLUL 3. EXCEPTII
                                                                   ¸

     int v[] = new int[10];
     try {
       v[10] = 0;
     } catch (ArrayIndexOutOfBoundsException e) {
       System.out.println("Atentie la indecsi!");
       e.printStackTrace();
     } // Corect, programul continua

     v[11] = 0;
     /* Nu apare eroare la compilare
        dar apare exceptie la executie si
        programul va fi terminat.
     */
     System.out.println("Aici nu se mai ajunge...");


    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
                                                          s
generat˘ nici o exceptie, ci va fi furnizat ca rezultat o constant˘ care poate
        a             ¸                                          a
fi, functie de operatie, Infinity, -Infinity, sau Nan.
       ¸

     int a=1, int b=0;
     System.out.println(a/b); // Exceptie la executie !

     double x=1, y=-1, z=0;
     System.out.println(x/z); // Infinity
     System.out.println(y/z); // -Infinity
     System.out.println(z/z); // NaN


3.7       Crearea propriilor exceptii
                                  ¸
Adeseori poate 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       ¸                                                                  a
cazuri care nu au fost prevazute ˆ ierarhia exceptiilor standard Java.
                                  ın               ¸
O exceptie proprie trebuie s˘ se ˆ
        ¸                    a     ıncadreze ˆ a ˆ ierarhia exceptiilor Java,
                                             ıns˘ ın                ¸
cu alte cuvinte clasa care o implementeaz˘ trebuie s˘ fie subclas˘ a uneia
                                            a          a              a
deja existente ˆ aceasta ierarhie, preferabil una apropiat˘ ca semnificatie,
               ın                                           a               ¸
sau superclasa Exception.
3.7. CREAREA PROPRIILOR EXCEPTII
                             ¸                                            93

public 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-
                                   ¸          a
tiv de scoatere a elementului din vˆrful stivei. Dac˘ presupunem c˘ stiva
                                     a                a              a
poate memora maxim 100 de elemente, ambele operatii pot provoca exceptii.
                                                     ¸                    ¸
Pentru a personaliza aceste exceptii vom crea o clas˘ specific˘ denumit˘
                                   ¸                    a       a           a
ExceptieStiva:

                        Listing 3.3: Exceptii proprii
class ExceptieStiva extends Exception {
  public ExceptieStiva ( String mesaj ) {
    super ( mesaj ) ;
  }
}

class Stiva {

    int elemente [] = new int [100];
    int n =0; // numarul de elemente din stiva

    public void adauga ( int x ) throws ExceptieStiva {
      if ( n ==100)
        throw new ExceptieStiva ( " Stiva este plina ! " ) ;
      elemente [ n ++] = x ;
    }

    public int scoate () throws ExceptieStiva {
      if ( n ==0)
        throw new ExceptieStiva ( " Stiva este goala ! " ) ;
      return elemente [n - -];
    }
}

    Secventa cheie este extends Exception care specific˘ faptul c˘ noua
          ¸                                               a        a
clas˘ ExceptieStiva este subclas˘ a clasei Exception ¸i deci implementeaz˘
    a                           a                    s                   a
obiecte ce reprezint˘ exceptii.
                    a      ¸
94                                             CAPITOLUL 3. EXCEPTII
                                                                 ¸

In general, codul 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                             s
Procesul de creare a unei noi exceptii poate fi dus mai departe prin ad˘ugarea
                                    ¸                                  a
unor noi metode clasei ce descrie acea exceptie, ˆ a aceasta dezvoltare nu
                                               ¸ ıns˘
ˆsi are rostul ˆ majoritatea cazurilor. Exceptiile proprii sunt descrise uzual
ı¸             ın                              ¸
de clase foarte simple, chiar 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˘
Capitolul 4

Intr˘ri ¸i ie¸iri
    a s      s

4.1      Introducere
4.1.1     Ce sunt fluxurile?
Majoritatea aplicatiilor necesit˘ citirea unor informatii care se g˘sesc pe
                    ¸            a                        ¸             a
o surs˘ extern˘ sau trimiterea unor informatii c˘tre o destinatie extern˘.
        a       a                               ¸ a                 ¸         a
Informatia se poate g˘si oriunde: ˆ
          ¸             a           ıntr-un fi¸ier pe disc, ˆ retea, ˆ memorie
                                              s             ın ¸     ın
sau ˆ alt program ¸i poate fi de orice tip: date primitive, obiecte, imagini,
     ın              s
sunete, etc. Pentru a aduce informatii dintr-un mediu extern, un progam Java
                                     ¸
trebuie s˘ deschid˘ un canal de comunicatie (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                ¸         a
deschizˆnd un canal de comunicatie (flux) c˘tre acea destinatie ¸i scriind
         a                          ¸           a                  ¸ s
secvential informatiile respective.
        ¸          ¸
    Indiferent de tipul informatiilor, citirea/scrierea de pe/c˘tre un mediu
                                ¸                                a
extern 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
96                                                     ˘
                                      CAPITOLUL 4. INTRARI SI IESIRI
                                                           ¸    ¸

   Definitii:
         ¸
Un flux este un canal de comunicatie unidirectional ˆ
                                    ¸          ¸    ıntre dou˘ procese.
                                                             a
Un proces care descrie o surs˘ extern˘ de date se nume¸te proces produc˘tor.
                             a        a                s                a
Un proces care descrie o destinatie extern˘ pentru date se nume¸te proces
                                  ¸        a                      s
consumator.
Un flux care cite¸te date se nume¸te flux de intrare.
                s                 s
Un 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.
                       ¸                    a
Fiecare flux are un singur proces produc˘tor ¸i un singur proces consumator.
                                         a    s
Intre dou˘ procese pot exista oricˆte fluxuri, orice proces putˆnd fi atˆt pro-
          a                       a                            a      a
ducator cˆt ¸i consumator ˆ acela¸i timp, dar pe fluxuri diferite.
          a s              ın       s
Consumatorul ¸i producatorul nu comunic˘ direct printr-o interfat˘ de flux
               s                           a                       ¸a
ci prin intermediul codului Java de tratare a fluxurilor.



   Clasele ¸i intefetele standard pentru lucrul cu fluxuri se g˘sesc ˆ pachetul
           s        ¸                                         a     ın
java.io. Deci, orice program care necesit˘ operatii de intrare sau ie¸ire tre-
                                           a       ¸                   s
buie s˘ contin˘ instructiunea de import a pachetului java.io:
      a    ¸ a           ¸
                             import java.io.*;

4.1.2      Clasificarea fluxurilor
Exist˘ 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         ¸
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 datelor


4.1.3    Ierarhia claselor pentru lucrul cu fluxuri
Clasele 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.
                                s

Acestea sunt superclase abstracte pentru toate clasele ce implementeaz˘      a
fluxuri 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     ¸                        ın
BufferedReader, 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                                 ıi
corespunde 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
                                                                  a
specializate pentru citirea/scrierea datelor pe 8 biti. Ca ¸i ˆ cazul flux-
                                                    ¸      s ın
urilor pe caractere denumirile claselor vor avea terminatia superclasei lor:
                                                         ¸
FileInputStream, BufferedInputStream, FileOutputStream,
BufferedOutputStream, etc., fiec˘rui flux de intrare XInputStream core-
                                   a
spunzˆndu-i uzual un flux de ie¸ire XOutputStream, f˘r˘ ca acest lucru s˘
      a                          s                      aa                a
fie obligatoriu.
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       a
mediul 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 fluxurilor
Superclasele abstracte Reader ¸i InputStream definesc metode similare pen-
                              s
tru citirea datelor.

     Reader               InputStream
     int read()           int read()
     int read(char buf[]) int read(byte buf[])
     ...                  ...

   De asemenea, ambele clase pun la dispozitie metode pentru marcarea
                                               ¸
unei locatii ˆ
         ¸ ıntr-un 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,
                                 s
definind 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                               ın
aceasta nu este apelat˘ explicit, fluxul va fi automat ˆ
                      a                               ınchis de c˘tre colectorul
                                                                 a
de 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,
                                                       a
datele 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
4.2. FOLOSIREA FLUXURILOR                                                    99

4.2     Folosirea fluxurilor
A¸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                                     s
procesarea datelor (de filtrare). In continuare, vom vedea care sunt cele mai
importante clase din cele dou˘ categorii ¸i la ce folosesc acestea, precum ¸i
                                a          s                               s
modalit˘¸ile de creare ¸i utilizare a fluxurilor.
       at              s


4.2.1    Fluxuri primitive
Fluxurile primitive sunt responsabile cu citirea/scrierea efectiv˘ a datelor,
                                                                 a
punˆnd la dispozitie implement˘ri ale metodelor de baz˘ read, respectiv
   a                ¸           a                          a
write, 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
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 procesare
Fluxurile de procesare (sau de filtrare) sunt responsabile cu preluarea datelor
de la un flux primitiv ¸i procesarea acestora pentru a le oferi ˆ
                       s                                       ıntr-o alt˘ form˘,
                                                                         a     a
mai util˘ dintr-un anumit punct de vedere. De exemplu, BufferedReader
         a
poate prelua date de la un 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                                                                    a
cu caracter. Un flux de procesare nu poate fi folosit decˆt ˆ a ımpreun˘ cu un
                                                                         a
flux 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
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 flux
Orice flux este un obiect al clasei ce implementeaz˘ fluxul respectiv. Crearea
                                                   a
unui 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:
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
                                                   aa
un flux primitiv de citire/scriere a datelor. Din acest motiv, constructorii
claselor pentru fluxurile de procesare nu primesc ca argument un dispozitiv
extern de memorare a datelor ci o referinta la un flux primitiv responsabil
                                           ¸
cu citirea/scrierea efectiv˘ a datelor:
                           a

Exemple:
  //crearea unui flux de intrare printr-un buffer
  BufferedReader in = new BufferedReader(
      new FileReader("fisier.txt"));
  //echivalent cu
  FileReader fr = new FileReader("fisier.txt");
  BufferedReader in = new BufferedReader(fr);

  //crearea unui flux de iesire printr-un buffer
  BufferedWriter out = new BufferedWriter(
      new FileWriter("fisier.txt")));
  //echivalent cu
  FileWriter fo = new FileWriter("fisier.txt");
  BufferedWriter out = new BufferedWriter(fo);

A¸adar, crearea unui flux pentru procesarea datelor are formatul general:
 s
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
                                    s
Fluxurile pentru lucrul cu fi¸iere sunt cele mai usor de ˆ
                                s                             ınteles, ˆ
                                                                       ıntrucˆt
                                                                             a
operatia lor de baz˘ este citirea, respectiv scrierea unui caracter sau octet
      ¸              a
dintr-un sau ˆıntr-un fi¸ier specificat uzual prin numele s˘u complet sau relativ
                       s                                 a
la directorul curent.
    Dup˘ cum am v˘zut deja, clasele care implementeaz˘ aceste fluxuri sunt
        a            a                                     a
urm˘toarele:
    a

  FileReader, FileWriter            - caractere
  FileInputStream, FileOutputStream - octeti

   Constructorii acestor clase accept˘ ca argument un obiect care s˘ specifice
                                     a                             a
un anume fi¸ier. Acesta poate fi un ¸ir de caractere, on obiect de tip File
            s                         s
sau un obiect de tip FileDesciptor (vezi ”Clasa File”).
   Constructorii clasei FileReader sunt:

  public FileReader(String fileName)
      throws FileNotFoundException
  public FileReader(File file)
      throws FileNotFoundException
  public FileReader(FileDescriptor fd)

   Constructorii clasei FileWriter:

  public FileWriter(String fileName)
      throws IOException
  public FileWriter(File file)
      throws IOException
  public FileWriter(FileDescriptor fd)
  public FileWriter(String fileName, boolean             append)
      throws IOException
104                                                          ˘
                                            CAPITOLUL 4. INTRARI SI IESIRI
                                                                 ¸    ¸

    Cei mai uzuali constructori sunt cei care primesc ca argument numele
fi¸ierului. Ace¸tia pot provoca exceptii de tipul FileNotFoundException ˆ
 s            s                     ¸                                   ın
cazul ˆ care fi¸ierul cu numele specificat nu exist˘. Din acest motiv orice
      ın        s                                  a
creare a unui flux de acest tip trebuie f˘cut˘ ˆ
                                         a a ıntr-un bloc try-catch sau
metoda ˆ 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                                           ¸             s
cu numele ”in.txt” ˆ
                   ıntr-un alt fi¸ier cu numele ”out.txt”. Ambele fi¸iere sunt
                                s                                 s
considerate ˆ directorul curent.
            ın

                             Listing 4.1: Copierea unui fisier
import java . io .*;
public class Copiere {
  public static void main ( String [] args )              {

        try {
          FileReader in = new FileReader ( " in . txt " ) ;
          FileWriter out = new FileWriter ( " out . txt " ) ;

          int c ;
          while (( c = in . read () ) != -1)
            out . write ( c ) ;

          in . close () ;
          out . close () ;

        } catch ( IOException e ) {
          System . err . println ( " Eroare la operatiile cu fisiere ! " ) ;
          e . printStackTrace () ;
        }
    }
}

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

scris. Dac˘ doream s˘ facem operatia de ad˘ugare(append) ¸i nu de rescriere
           a         a             ¸      a              s
pentru fi¸ierul ”out.txt” foloseam:
         s

        FileWriter out = new FileWriter("out.txt", true);

4.2.5     Citirea ¸i scrierea cu buffer
                  s
Clasele 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                ın
sul de citire/scriere a informatiilor, reducˆnd astfel numarul de acces˘ri ale
                                 ¸            a                            a
dispozitivului 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                              a
folosirea 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˘a
tampon. 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
                            ın
flux ¸i, odat˘ cu citirea caracterului, vor fi memorati ˆ buffer ¸i caracterele
     s        a                                         ın         s
care ˆ urmeaz˘. Evident, BufferedInputStream functioneaz˘ dup˘ acela¸i
      ıi          a                                      ¸       a      a      s
principiu, singura diferent˘ fiind faptul c˘ sunt cititi octeti.
                            ¸a               a       ¸       ¸
    Similar lucreaza ¸i clasele BufferedWriter ¸i BufferedOutputStream.
                        s                          s
La operatiile de scriere datele scrise nu vor ajunge direct la destinatie, ci vor
          ¸                                                            ¸
fi memorate jntr-un buffer de o anumit˘ dimensiune. Atunci cˆnd bufferul
                                            a                       a
este plin, continutul acestuia va fi transferat automat la destinatie.
                ¸                                                    ¸
    Fluxurile de citire/scriere cu buffer sunt fluxuri de procesare ¸i sunt s
folosite prin suprapunere cu alte fluxuri, dintre care obligatoriu unul este
primitiv.

  BufferedOutputStream out = new BufferedOutputStream(
    new FileOutputStream("out.dat"), 1024)
  //1024 este dimensiunea bufferului

   Constructorii cei mai folositi ai acestor clase sunt urm˘torii:
                               ¸                           a

  BufferedReader(Reader in)
  BufferedReader(Reader in, int dim_buffer)
  BufferedWriter(Writer out)
106                                                    ˘
                                      CAPITOLUL 4. INTRARI SI IESIRI
                                                           ¸    ¸

    BufferedWriter(Writer out, int dim_buffer)
    BufferedInputStream(InputStream in)
    BufferedInputStream(InputStream in, int dim_buffer)
    BufferedOutputStream(OutputStream out)
    BufferedOutputStream(OutputStream out, int dim_buffer)

   In cazul constructorilor ˆ care dimensiunea buffer-ului nu este specificat˘,
                             ın                                              a
aceasta 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             a
acestea, clasele pentru scriere prin buffer mai au ¸i metoda flush care gole¸te
                                                  s                        s
explicit zona tampon, chiar dac˘ aceasta nu este plin˘.
                                  a                    a

    BufferedWriter out = new BufferedWriter(
      new FileWriter("out.dat"), 1024)
    //am creat un flux cu buffer de 1024 octeti
    for(int i=0; i<1000; i++)
      out.write(i);
    //bufferul nu este plin, in fisier nu s-a scris nimic
    out.flush();
    //bufferul este golit, datele se scriu in fisier



    Metoda readLine
Este specific˘ fluxurilor de citire cu buffer ¸i permite citirea linie cu linie a
             a                               s
datelor de intrare. O linie reprezint˘ o succesiune de caractere terminat˘ cu
                                     a                                    a
simbolul pentru sfˆr¸it de linie, dependent de platforma de lucru. Acesta
                    as
este reprezentat ˆ Java prin secventa escape ’n’;
                 ın                  ¸

    BufferedReader br = new BufferedReader(new FileReader("in"))
    String linie;
    while ((linie = br.readLine()) != null) {
      ...
      //proceseaza linie
    }
    br.close();
}
4.2. FOLOSIREA FLUXURILOR                                                   107

4.2.6     Concatenarea fluxurilor
Clasa SequenceInputStream permite unei aplicatii s˘ combine serial mai
                                                           a
multe fluxuri de intrare astfel ˆ at acestea s˘ apar˘ ca un singur flux de
                                 ıncˆ             a      a
intrare. Citirea datelor dintr-un astfel de flux se face astfel: se cite¸te din
                                                                        s
primul flux de intrare specificat pˆna cˆnd se ajunge la sfˆrsitul acestuia,
                                     a     a                    a
dup˘ care primul flux de intrare este ˆ
    a                                  ınchis ¸i se deschide automat urm˘torul
                                              s                           a
flux de intrare din care se vor citi ˆ continuare datele, dup˘ care procesul se
                                    ın                        a
repet˘ 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                         a
tipul InputStream.
Cel de-al doilea construie¸te un flux de intrare care combin˘ doar dou˘ fluxuri
                          s                                 a         a
s1 ¸i s2, primul flux citit fiind s1.
   s
    Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea a
dou˘ 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 ) ;
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 datelor
Un flux de filtrare se ata¸eaz˘ altui flux pentru a filtra datele care sunt
                            s a
citite/scrise de c˘tre acel flux. Clasele pentru filtrarea datelor superclasele
                  a
abstracte:

    • FilterInputStream - pentru filtrarea fluxurilor de intrare ¸i
                                                               s

    • FilterOutputStream - pentru filtrarea fluxurilor de ie¸ire.
                                                          s

   Cele mai importante fluxruri pentru filtrarea datelor sunt implementate
de clasele:

    DataInputStream, DataOutputStream
    BufferedInputStream, BufferedOutputStream
    LineNumberInputStream
    PushbackInputStream
    PrintStream
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      a
interpretate sub alt˘ form˘. A¸a cum am v˘zut la citirea/scrierea cu zon˘
                    a      a     s           a                             a
tampon, clasele de filtrare BufferedInputStream ¸i BufferedOutputStream
                                                  s
colecteaz˘ datele unui flux ˆ
          a                  ıntr-un buffer, urmˆnd ca citirea/scrierea s˘ se
                                               a                        a
fac˘ prin intermediu acelui buffer.
   a

    A¸adar, fluxurile de filtrare nu elimin˘ date citite sau scrise de un anumit
      s                                  a
flux, ci introduc o noua modalitate de manipulare a lor, ele mai fiind numite
¸i fluxuri de procesare. Din acest motiv, fluxurile de filtrare vor contine
s                                                                          ¸
anumite metode specializate pentru citirea/scrierea datelor, altele decˆt cele
                                                                         a
comune tuturor fluxurilor. De exemplu, clasa BufferedInputStream pune
la 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
                                                  s
ocup˘ efectiv de citirea/scrierea datelor:
    a
FluxFiltrare numeFlux = new FluxFiltrare(referintaAltFlux);




4.2.8     Clasele DataInputStream ¸i DataOutputStream
                                  s


Aceste clase ofer˘ metode prin care un flux nu mai este v˘zut ca o ˆ
                  a                                           a        ınsiruire
de octeti, ci de date primitive. Prin urmare, vor furniza metode pentru
         ¸
citirea ¸i scrierea datelor la nivel de tip primitiv ¸i nu la nivel de octet.
        s                                               s
Clasele care ofer˘ un astfel de suport implementeaz˘ interfetele DataInput,
                  a                                     a       ¸
respectiv DataOutput. Acestea definesc metodele pe care trebuie s˘ le pun˘ la
                                                                     a       a
dispozitie ˆ vederea citireii/scrierii datelor de tip primitiv. Cele mai folosite
        ¸ ın
metode, altele decˆt cele comune tuturor fluxurilor, sunt date ˆ tabelul de
                    a                                              ın
mai jos:
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-
                                                           s
cate de interfetele DataInput ¸i DataOutput ¸i pot provoca exceptii de tipul
                                s               s                     ¸
IOException. Denumirile lor sunt sugestive pentru tipul de date pe care ˆ      ıl
prelucreaz˘. mai putin readUTF ¸i writeUTF care se ocup˘ cu obiecte de tip
            a         ¸            s                           a
String, fiind singurul tip referint˘ permis de aceste clase.
                                   ¸a
     Scrierea datelor folosind fluxuri de acest tip se face ˆ format binar, ceea
                                                           ın
ce ˆı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                       s
DataInputStream ¸i DataOutputStream permit serializarea tipurilor prim-
                     s
itive ¸i a ¸irurilor de caractere. Serializarea celorlalte tipuri referint˘ va fi
       s    s                                                             ¸a
f˘cut˘ prin intermediul altor clase, cum ar fi ObjectInputStream ¸i
 a a                                                                    s
ObjectOutputStream (vezi ”Serializarea obiectelor”).


4.3      Intr˘ri ¸i ie¸iri formatate
             a s      s
Incepˆnd cu versiunea 1.5, limbajul Java pune la dispozitii modalit˘¸i sim-
      a                                                  ¸          at
plificate pentru afi¸area formatat˘ a unor informatii, respectiv pentru citirea
                  s             a               ¸
de date formatate de la tastatur˘.
                                a

4.3.1     Intr˘ri formatate
              a
Clasa 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
                                                  a
al constructorului fluxul System.in:
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
            s
Clasele PrintStream ¸i PrintWriter pun la dispozitiile, pe lˆng˘ metodele
                    s                              ¸        a a
print, println care ofereau posibilitatea de a afi¸a un ¸ir de caractere, ¸i
                                                 s      s                s
metodele format, printf (echivalente) ce permit afi¸area formatat˘ a unor
                                                   s             a
variabile.

  System.out.printf("%s %8.2f %2d %n", nume, salariu, varsta);

Formatarea ¸irurilor de caractere se bazeaz˘ pe clasa java.util.Formatter.
           s                               a


4.4      Fluxuri standard de intrare ¸i ie¸ire
                                     s    s
Mergˆnd pe linia introdus˘ de sistemul de operare UNIX, orice program Java
      a                  a
are :

   • 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 s
descriu fluxuri de date care comunic˘ cu dispozitivele standard ale sistemului.
                                    a
Aceste 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
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                                                                  s
el 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         ¸                    ¸                          s
de 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      aa
este PrintStream, clas˘ concret˘ pentru scrierea datelor. In schimb, fluxul
                        a         a
standard de intrare System.out este de tip InputStream, care este o clas˘      a
abstract˘, deci pentru a-l putea utiliza eficient va trebui sa-l folosim ˆ
        a                                                               ımpreuna
cu un flux de procesare(filtrare) care s˘ permit˘ citirea facil˘ a datelor.
                                         a        a              a

4.4.2     Citirea datelor de la tastatur˘
                                        a
Uzual, vom dori s˘ folosim metoda readLine pentru citirea datelor de la
                    a
tastatura ¸i din acest motiv vom folosi intrarea standard ˆ
          s                                               ımpreun˘ cu o clas˘
                                                                 a          a
de procesare care ofer˘ aceast˘ metod˘. Exemplul tipic este:
                       a       a       a

  BufferedReader stdin = new BufferedReader(
    new InputStreamReader(System.in));
  System.out.print("Introduceti o linie:");
  String linie = stdin.readLine()
  System.out.println(linie);

    In exemplul urm˘tor este prezentat un program care afi¸eaza liniile intro-
                     a                                     s
duse de la tastatur˘ pˆn˘ ˆ momentul ˆ care se introduce linia ”exit” sau
                   a a a ın              ın
o linie vid˘ ¸i mentioneaz˘dac˘ ¸irul respectiv reprezint˘ un num˘r sau nu.
           as      ¸      a as                           a       a
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                                             a
de la tastatur˘ este folosirea clasei java.util.Scanner.
              a


4.4.3     Redirectarea fluxurilor standard
Redirectarea fluxurilor standard presupune stabilirea unei alte surse decˆt    a
tastatura pentru citirea datelor, respectiv alte destinatii decˆt ecranul pentru
                                                        ¸      a
cele dou˘ fluxuri de ie¸ire. In clasa System exist˘ urm˘toarele metode statice
        a              s                          a     a
care realizeaz˘ acest lucru:
              a

  setIn(InputStream) - redirectare intrare
  setOut(PrintStream) - redirectare iesire
  setErr(PrintStream) - redirectare erori
114                                                      ˘
                                        CAPITOLUL 4. INTRARI SI IESIRI
                                                             ¸    ¸

Redirectarea ie¸irii este util˘ ˆ special atunci cˆnd sunt afi¸ate foarte multe
               s              a ın                a          s
date pe ecran. Putem redirecta afisarea c˘tre un fi¸ier pe care s˘-l citim dup˘
                                           a        s           a            a
executia programului. Secventa clasic˘ de redirectare a ie¸irii este c˘tre un
      ¸                          ¸      a                   s          a
fi¸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-o
manier˘ 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                                             ın
sol˘ care prime¸te mai multe valori de intrare. Pentru a nu le scrie de la
   a            s
tastatur˘ 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                     a
testarea programului a luat sfˆrsit redirectarea poate fi eliminat˘, datele fiind
                              a                                  a
cerute din nou de la tastatur˘.
                              a

               Listing 4.4: Exemplu de folosire a redirect˘rii:
                                                          a
import java . io .*;
class Redirectare {
  public static void main ( String [] args ) {
    try {
      BufferedInputS t r ea m in = new B uf f e re d I np u tS t r ea m (
        new FileInputStream ( " intrare . txt " ) ) ;
      PrintStream out = new PrintStream (
          new FileOutputStream ( " rezultate . txt " ) ) ;
      PrintStream err = new PrintStream (
          new FileOutputStream ( " erori . txt " ) ) ;

        System . setIn ( in ) ;
        System . setOut ( out ) ;
        System . setErr ( err ) ;

        BufferedReader br = new BufferedReader (
          new InputStream Reader ( System . in ) ) ;
4.4. FLUXURI STANDARD DE INTRARE SI IESIRE
                                 ¸    ¸                                      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)
                           a
Clasa 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            a
octeti sau caractere, se vor citi, pe rˆnd, atomii lexicali ai fluxului respectiv.
    ¸                                  a
Printr-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 separator

Atomii lexicali sunt desp˘rtiti ˆ
                          a ¸ ıntre ei de separatori. Implicit, ace¸ti separa-
                                                                      s
tori 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:
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
                        s
variabilelor:

   • 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˘      a
tipul atomului lexical citit ¸i scrie ˆ variabilele nval sau sval valoarea core-
                             s        ın
spunzato˘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
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                                  a
pˆna se ajunge la sfˆrsitul fluxului (TT EOF). In cadrul buclei ”while” se deter-
 a                  a
min˘ tipul atomul curent curent (ˆ
    a                                 ıntors de metoda nextToken) ¸i apoi se afl˘
                                                                  s            a
valoarea 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                     a
citim 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                   ın
String.


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                                      ¸                 s
Acestea 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
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 IOException
RandomAccessFile(StringnumeFisier, StringmodAcces)
  throws IOException
unde 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                                 s
Exemple:
  RandomAccesFile f1 = new RandomAccessFile("fisier.txt", "r");
  //deschide un fisier pentru citire

  RandomAccesFile f2 = new RandomAccessFile("fisier.txt", "rw");
  //deschide un fisier pentru scriere si citire
   Clasa RandomAccesFile suport˘ notiunea de pointer de fi¸ier. Acesta este
                                   a ¸                      s
un indicator ce specific˘ pozitia curent˘ ˆ fi¸ier. La deschiderea unui fi¸ier
                        a     ¸         a ın s                          s
pointerul are valoarea 0, indicˆnd ˆ
                                a ınceputul fi¸ierului. Apeluri la metodele
                                                s
de 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                                s
metode 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
4.6. CLASA FILE                                                            119

4.6     Clasa File
Clasa File nu se refer˘ doar la un fi¸ier ci poate reprezenta fie un fi¸ier
                               a               s                            s
anume, fie multimea fi¸ierelor dintr-un director.
                            s
    Specificarea unui fi¸ier/director se face prin specificarea c˘ii absolute spre
                            s                                  a
acel fi¸ier sau a c˘ii relative fat˘ de directorul curent. Acestea trebuie s˘ re-
       s            a                ¸a                                    a
specte conventiile de specificare a c˘ilor ¸i numelor fi¸ierelor de pe platforma
                ¸                            a   s      s
de lucru.
Utilitate clasei File const˘ ˆ furnizarea unei modalit˘¸i de a abstractiza
                                  a ın                     at
dependentele cailor ¸i numelor fi¸ierelor fat˘de ma¸ina gazd˘, precum ¸i
           ¸              s                 s      ¸a    s        a           s
punerea la dispozitie a unor metode pentru lucrul cu fisere ¸i directoare la
                      ¸                                         s
nivelul sistemului de operare.
    Astfel, ˆ aceast˘ clas˘ vom g˘si metode pentru testarea existentei, ¸tergerea,
             ın        a        a        a                           ¸ s
redenumirea unui fi¸ier sau director, crearea unui director, listarea fi¸ierelor
                        s                                                s
dintr-un director, etc.
    Trebuie mentionat ¸i faptul c˘ majoritatea constructorilor fluxurilor care
                  ¸          s            a
permit accesul la fi¸iere accept˘ ca argument un obiect de tip File ˆ locul
                       s              a                                 ın
unui ¸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
                                                                      s
prezentate prin intermediul exemplului urm˘tor care listeaz˘ fi¸ierele ¸i sub-
                                             a                a s       s
directoarele unui director specificat ¸i, pentru fiecare din ele afi¸eaz˘ diverse
                                     s                           s a
informatii:
        ¸

               Listing 4.6: Listarea continutului unui director
                                        ¸
/* Programul listeaza fisierele si subdirectoarele unui
   director .
   Pentru fiecare din ele vor fi afisate diverse informatii .
   Numele directorului este primit ca argument de la
   linia de comanda , sau este directorul curent .
*/

import java . io .*;
120                                                          ˘
                                            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 () ;
        }
    }
}
Capitolul 5

Interfete
       ¸

5.1     Introducere

5.1.1    Ce este o interfat˘ ?
                          ¸a

Interfetele duc conceptul de clas˘ abstract˘ cu un pas ˆ
       ¸                         a         a           ınainte prin eliminarea
oric˘ror implement˘ri de metode, punˆnd ˆ practic˘ unul din conceptele
    a               a                    a    ın       a
program˘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              ¸a
protocol de comunicare ˆıntre obiecte.
    O interfat˘ Java define¸te un set de metode dar nu specific˘ nici o imple-
             ¸a           s                                   a
mentare pentru ele. O clas˘ care implementeaz˘ o interfat˘ trebuie obligato-
                           a                  a         ¸a
riu s˘ specifice implement˘ri pentru toate metodele interfetei, supunˆndu-se
     a                    a                               ¸         a
a¸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
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                          ¸a
public˘ este accesibil˘ tuturor claselor, indiferent de pachetul din care fac
       a               a
parte, implicit nivelul de acces fiind doar la nivelul pachetului din care face
parte interfata.
             ¸
    O interfat˘ poate extinde oricˆte interfete. Acestea se numesc superinterfete
             ¸a                   a          ¸                                 ¸
¸i sunt separate prin virgul˘. (vezi ”Mo¸tenirea multipl˘ prin intermediul
s                            a              s                a
interfetelor”).
       ¸
    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
      }
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.
               a




5.2.2    Implementarea unei interfete
                                   ¸
Implementarea uneia sau mai multor interfete de c˘tre o clas˘ se face prin
                                          ¸      a          a
intermediul cuvˆntului cheie implements:
               a
  class NumeClasa implements NumeInterfata
  sau
  class NumeClasa implements Interfata1, Interfata2, ...
124                                            CAPITOLUL 5. INTERFETE
                                                                   ¸

    O clas˘ poate implementa oricˆte interfete sau poate s˘ nu implementeze
           a                      a         ¸             a
nici una.
    In cazul ˆ care o clas˘ implementeaz˘ o anumit˘ interfat˘, atunci tre-
             ın            a              a          a        ¸a
buie 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                                        ¸a
mai trebuie modificat˘, ˆ sensul c˘ ad˘ugarea unor metode noi sau schim-
                       a ın         a a
barea signaturii metodelor existente vor duce la erori ˆ compilarea claselor
                                                       ın
care o implementeaz˘. Evident, o clas˘ poate avea ¸i alte metode ¸i variabile
                     a                a            s              s
membre ˆ afar˘ de cele definite ˆ interfat˘.
          ın    a                ın       ¸a


   Atentie
        ¸
   Modificarea unei interfete implic˘ modificarea tuturor claselor care im-
                            ¸      a
plementeaz˘ acea interfat˘.
          a             ¸a


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


5.2.3     Exemplu: implementarea unei stive
S˘ consider˘m urm˘torul exemplu. Dorim s˘ implement˘m un nou tip de
 a           a        a                        a             a
date numit Stack, care s˘ modeleze notiunea de stiv˘ de obiecte. Obiectele
                          a             ¸              a
de tip stiv˘, indiferent de implementarea lor, vor trebui s˘ contin˘ metodele:
           a                                               a    ¸ a

   • push - adaug˘ un nou element in 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
5.2. FOLOSIREA INTERFETELOR
                      ¸                                                  125

    Din punctul de vedere al structurii interne, o stiv˘ poate fi implementat˘
                                                       a                    a
folosind un vector sau o list˘ ˆ antuit˘, ambele solutii avˆnd avantaje ¸i
                              a ınl˘ ¸ a                  ¸    a            s
dezavantaje. Prima solutie este mai simplu de ˆ ¸eles, ˆ timp ce a doua este
                        ¸                       ınt      ın
mai eficient˘ din punctul de vedere al folosirii memoriei. Deoarece nu dorim
            a
s˘ leg˘m tipul de date Stack de o anumit˘ implementare structural˘, ˆ vom
 a    a                                    a                         a ıl
defini 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˘ma
s˘ 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˘
                                          ¸                  a
public 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
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        ¸a
modificatorul public, ele sunt totu¸i publice ¸i trebuie declarate ca atare ˆ
                                    s        s                             ın
clas˘.
    a
Trebuie remarcat ¸i faptul c˘ metoda toString este definit˘ deja ˆ clasa
                  s          a                               a       ın
Object, 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
                         a
este de fapt supradefinirea ei.
5.2. FOLOSIREA INTERFETELOR
                      ¸                                               127

O alt˘ observatie important˘ se refer˘ la faptul c˘ trebuie s˘ declar˘m ˆ
      a        ¸              a       a           a          a       a ın
cadrul interfetei ¸i exceptiile aruncate de metode, ce trebuie obligatoriu
              ¸ s         ¸
tratate.
    S˘ vedem acum modalitatea de implementare a stivei folosind o list˘
     a                                                                   a
ˆ antuit˘:
ınl˘ ¸ a

             Listing 5.4: Implementarea stivei folosind o list˘
                                                              a
// Implementarea stivei folosind o lista inlantuita .
public class StackImpl2 implements Stack {

  class Node {
    // Clasa interna ce reprezinta un nod al listei
    Object item ; // informatia din nod
    Node link ;     // legatura la urmatorul nod
    Node ( Object item , Node link ) {
       this . item = item ;
       this . link = link ;
    }
  }

  private Node top = null ;
  // Referinta la varful stivei

  public void push ( Object item ) {
      Node node = new Node ( item , top ) ;
      top = node ;
  }
    public void pop () throws StackException {
       if ( empty () )
         throw new StackException ( " Stiva este vida ! " ) ;
       top = top . link ;
    }
    public Object peek () throws StackException {
       if ( empty () )
         throw new StackException ( " Stiva este vida ! " ) ;
       return top . item ;
    }
    public boolean empty () {
       return ( top == null ) ;
    }
    public String toString () {
       String s = " " ;
       Node node = top ;
       while ( node != null ) {
128                                                 CAPITOLUL 5. INTERFETE
                                                                        ¸

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

    Singura observatie pe care o facem aici este c˘, de¸i metoda push din
                     ¸                                a  s
interfat˘ 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          a
nu 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            s
tipul de date nou creat ¸i cele dou˘ implement˘ri ale sale:
                         s          a             a

                               Listing 5.5: Folosirea stivei
public class TestStiva {

    public static void afiseaza ( Stack s ) {
      System . out . println ( " Continutul stivei este : " + s ) ;
    }

    public static void main ( String args []) {
      try {
        Stack s1 = new StackImpl1 () ;
        s1 . push ( " a " ) ;
        s1 . push ( " b " ) ;
        afiseaza ( s1 ) ;

              Stack s2 = new StackImpl2 () ;
              s2 . push ( new Integer (1) ) ;
              s2 . push ( new Double (3.14) ) ;
              afiseaza ( s2 ) ;


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

   Observati folosirea interfetei Stack ca un tip de date, ce aduce flexibilitate
           ¸                  ¸
sporit˘ ˆ manevrarea claselor ce implementeaz˘ tipul respectiv. Metoda
      a ın                                         a
5.3. INTERFETE SI CLASE ABSTRACTE
            ¸ ¸                                                              129

afiseaza accept˘ ca argument orice obiect al unei clase ce implementeaz˘
               a                                                       a
Stack.

    Observatie¸
    In pachetul java.util exist˘ clasa Stack care modeleaz˘ notiune de stiv˘
                                 a                             a ¸              a
de obiecte ¸i, evident, aceasta va 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     as
pur demonstrativ.



5.3      Interfete ¸i clase abstracte
                ¸ s
La prima vedere o interfat˘ nu este altceva decˆt o clas˘ abstract˘ ˆ care
                           ¸a                     a        a         a ın
toate metodele sunt abstracte (nu au nici o implementare).
A¸adar, o clas˘ abstract˘ nu ar putea ˆ
  s            a         a             ınlocui o interfat˘ ?
                                                        ¸a
Raspunsul la intrebare depinde de situatie, ˆ a ˆ general este ’Nu’.
                                         ¸ ıns˘ ın
Deosebirea const˘ ˆ faptul c˘ unele clase sunt fortate s˘ extind˘ o anumit˘
                 a ın         a                     ¸     a       a           a
clas˘ (de exemplu orice applet trebuie s˘ fie subclasa a clasei Applet) ¸i nu ar
    a                                   a                              s
mai putea sa extind˘ o alt˘ clas˘, deoarece ˆ Java nu exista decˆt mo¸tenire
                    a      a     a          ın                    a     s
simpla. Fara folosirea interfetelor nu am putea forta clasa respectiv˘ s˘
                                ¸                      ¸                   a a
respecte diverse tipuri de protocoale.
    La nivel conceptual, diferenta const˘ ˆ
                                 ¸      a ın:

   • extinderea unei clase abstracte forteaz˘ o relatie ˆ
                                        ¸ a         ¸ ıntre clase;

   • implementarea unei interfete specific˘ doar necesitatea implement˘rii
                               ¸         a                           a
     unor anumie metode.

    In multe situatii interfetele ¸i clasele abstracte sunt folosite ˆ
                  ¸          ¸    s                                   ımpreun˘a
pentru a implementa cˆt mai flexibil ¸i eficient o anumit˘ ierarhie de clase. Un
                       a              s                  a
exemplu sugestiv este dat de clasele ce descriu colectii. Ca sa particulariz˘m,
                                                     ¸                      a
exist˘:
     a

   • interfata List care impune protocolul pe care trebuie s˘ ˆ respecte o
            ¸                                               a ıl
     clas˘ de tip list˘,
         a            a

   • clasa abstract˘ AbstractList care implementeaz˘ interfata List ¸i
                   a                                 a       ¸         s
     ofer˘ implement˘ri concrete pentru metodele comune tuturor tipurilor
         a           a
     de list˘,
            a
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                    a
interfete sau ca o interfat˘ s˘ extind˘ mai multe interfete (s˘ aib˘ mai multe
       ¸                  ¸a a        a                   ¸ a      a
superinterfete)
             ¸

  class NumeClasa implements Interfata1, Interfata2, ...
  interface NumeInterfata extends Interfata1, Interfata2, ...

O interfat˘ mosteneste 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                                     ¸ s
responsabilitatea 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();
¸              ˘
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       a
sau 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 stil
prost de programare. In cazul in care acest lucru se ˆ ampl˘, compilatorul
                                                       ıntˆ   a
nu va furniza eroare decˆt dac˘ se ˆ
                         a       a   ıncearc˘ referirea constantelor ambigue
                                            a
f˘r˘ a le prefixa cu numele interfetei sau dac˘ metodele cu acela¸i nume nu
 aa                                ¸          a                   s
pot fi deosbite, cum ar fi situatia cˆnd au aceea¸i list˘ de argumente dar
                                 ¸    a             s     a
tipuri 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
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˘.a


5.5      Utilitatea interfetelor
                           ¸
Dup˘ cum am v˘zut, o interfat˘ define¸te un protocol ce poate fi implementat
    a            a             ¸a       s
de 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 constante
Deoarece orice variabil˘ a unei interfete este implicit declarat˘ cu public,
                       a                ¸                         a
static si final, interfetele reprezint˘ o metod˘ convenabil˘ de creare a unor
                        ¸             a         a            a
grupuri de constante care s˘ fie folosite global ˆ
                            a                   ıntr-o aplicatie:
                                                             ¸
5.5. UTILITATEA INTERFETELOR
                       ¸                                                    133

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

5.5.2     Transmiterea metodelor ca parametri
Deoarece nu exist˘ pointeri propriu-zi¸i, transmiterea metodelor ca parametri
                    a                  s
este realizat˘ ˆ Java prin intermediul interfetelor. Atunci cˆnd o metod˘
              a ın                              ¸               a              a
trebuie s˘ primeasc˘ ca argument de intrare o referint˘ la o alt˘ functie
          a             a                                 ¸a           a     ¸
necesar˘ executiei sale, cunoscut˘ doar la momentul executiei, atunci 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 a
s                 a     ınt a      a         a
clarifica lucrurile.
    Primul exemplu se refer˘ la explorarea nodurilor unui graf. In fiecare
                             a
nod 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.
       ¸˘                                          a


interface Functie {
  public void executa(Nod u);
}

class Graf {
  //...
  void explorare(Functie f) {
    //...
134                                          CAPITOLUL 5. INTERFETE
                                                                 ¸

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

//Definim doua functii
class AfisareRo implements Functie {
  public void executa(Nod v) {
    System.out.println("Nodul curent este: " + v);
  }
}
class AfisareEn implements Functie {
  public void executa(Nod v) {
    System.out.println("Current node is: " + v);
  }
}

public class TestCallBack {
  public static void main(String args[]) {
    Graf G = new Graf();
    G.explorare(new AfisareRo());
    G.explorare(new AfisareEn());
  }
}
   Al doilea xemplu va fi prezentat ˆ sectiunea urm˘toare, ˆ
                                    ın    ¸         a        ıntrucˆt face
                                                                   a
parte din API-ul standard Java ¸i vor fi puse ˆ evident˘, prin intermediul
                                 s           ın      ¸a
s˘u, ¸i alte tehnici de programare.
 a s


5.6        Interfata FilenameFilter
                  ¸
Instantele claselor ce implementeaz˘ aceasta interfat˘ sunt folosite pentru a
      ¸                              a              ¸a
crea filtre pentru fi¸iere ¸i sunt primite ca argumente de metode care listeaz˘
                    s    s                                                   a
continutul 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
5.6. INTERFATA FILENAMEFILTER
            ¸                                                              135

    Interfata FilenameFilter are o singur˘ metod˘: accept care specific˘
            ¸                                  a    a                       a
criteriul de filtrare ¸i anume, testeaz˘ dac˘ numele fi¸ierului primit ca para-
                     s                 a     a        s
metru ˆ ı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                                                 a
primeasca criteriul de filtrare. In general, o clas˘ de specificare a unui filtru
                                                  a
are urm˘torul format:
        a

class FiltruFisiere implements FilenameFilter {
  String filtru;

    // Constructorul
    FiltruFisiere(String filtru) {
      this.filtru = filtru;
    }

    // Implementarea metodei accept
    public boolean accept(File dir, String nume) {
      if (filtrul este indeplinit)
        return true;
      else
        return false;
    }
}

   Metodele cele mai uzuale ale clasei String folosite pentru filtrarea fi¸ierelor
                                                                        s
sunt:

    • 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
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
                 ¸                                          a
clasa File:

    String[] list (FilenameFilter filtru)

    Observati c˘ aici interfata este folosit˘ ca un tip de date, ea fiind substi-
               a             ¸              a
tuit˘ cu orice clas˘ care o implementeaz˘. Acesta este un exemplu tipic de
    a              a                        a
transmitere 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     s
rectorul 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 ;
5.6. INTERFATA FILENAMEFILTER
            ¸                                                               137

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



5.6.1     Folosirea claselor anonime
In cazul ˆ care nu avem nevoie de clasa care reprezint˘ filtrul pentru listarea
         ın                                              a
fi¸ierelor dintr-un director decˆt o singur˘ dat˘, pentru a evita crearea unei
  s                             a           a     a
noi 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 () ;
      }
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 obiectelor
Am v˘zut ˆ primul capitol c˘ o solutie facil˘ ¸i eficient˘ de sortare a unui
      a    ın                 a       ¸       as        a
vector este folosirea metodei sort din clasa java.util.Arrays.

    int v[]={3, 1, 4, 2};
    java.util.Arrays.sort(v);
    // Sorteaza vectorul v
    // Acesta va deveni {1, 2, 3, 4}

    In cazul ˆ care elementele din vector sunt de tip primitiv, ca in exem-
             ın
plul de mai sus, nu exist˘ nici o problem˘ ˆ a determina ordinea fireasc˘
                          a                a ın                            a
a 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              ın
s˘ 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)
                                         aa
class Persoana {
  int cod ;
  String nume ;

    public Persoana ( int cod , String nume ) {
      this . cod = cod ;
      this . nume = nume ;
    }

    public String toString () {
       return cod + "  t " + nume ;
    }
}
5.7. COMPARAREA OBIECTELOR                                                139

    Programul urm˘tor ar trebui s˘ sorteze un vector de persoane:
                 a               a

                 Listing 5.9: Sortarea unui vector de tip referint˘
                                                                 ¸a
class Sortare {
  public static void main ( String args []) {
    Persoana p [] = new Persoana [4];
    p [0] = new Persoana (3 , " Ionescu " ) ;
    p [1] = new Persoana (1 , " Vasilescu " ) ;
    p [2] = new Persoana (2 , " Georgescu " ) ;
    p [3] = new Persoana (4 , " Popescu " ) ;

        java . util . Arrays . sort ( p ) ;

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


   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                               a
trebui, ˆ
        ıntr-un fel sau altul, s˘ specific˘m acest lucru.
                                a        a


5.7.1        Interfata Comparable
                    ¸
Interfata Comparable impune o ordine total˘ asupra obiectelor unei clase ce
       ¸                                  a
o implementeaz˘. Aceast˘ ordine se nume¸te ordinea natural˘ a clasei ¸i este
               a        a               s                 a          s
specificat˘ 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          ¸           a
metoda compareTo care trebuie s˘ returneze:
                                  a

    • o valoare strict negativ˘: dac˘ obiectul curent (this) este mai mic
                                 a   a
      decˆ obiectul primit ca argument;
         a

    • zero: dac˘ obiectul curent este egal decˆ obiectul primit ca argument;
               a                              a
140                                          CAPITOLUL 5. INTERFETE
                                                                 ¸

   • o valoare strict pozitiv˘: dac˘ obiectul curent este mai mare decˆ
                              a    a                                  a
     obiectul primit ca argument.

   Reamintim c˘ metoda equals, mo¸tenit˘ din Object de toate clasele,
                  a                    s      a
determin˘ dac˘ dou˘ obiecte sunt egale (au aceea¸i valoare). Spunem c˘
         a     a     a                              s                    a
ordinea natural˘ a unei clase C este consitent˘ cu equals dac˘ ¸i numai
                 a                              a              a s
dac˘ (e1.compareTo((Object)e2) == 0) are aceeas¸i valoare logic˘ cu
   a                                                  s            a
e1.equals((Object)e2, pentru orice e1, e2 instante ale lui C.
                                                   ¸
   null nu este instant˘ a nici unei clase ¸i e.compareTo(null) trebuie s˘
                        ¸a                 s                             a
arunce o exceptie de tip NullPointerException chiar dac˘ e.equals(null)
               ¸                                        a
returneaz˘ false.
          a
   S˘ presupunem c˘ dorim ca ordinea natural˘ a persoanelor s˘ fie dup˘
     a               a                           a              a        a
codul lor intern.

         Listing 5.10: Clasa Persoana cu suport pentru comparare
class Persoana implements Comparable {
  int cod ;
  String nume ;

  public Persoana ( int cod , String nume ) {
    this . cod = cod ;
    this . nume = nume ;
  }

  public String toString () {
     return cod + "  t " + nume ;
  }

  public boolean equals ( Object o ) {
    if (!( o instanceof Persoana ) )
      return false ;
    Persoana p = ( Persoana ) o ;
    return ( cod == p . cod ) && ( nume . equals ( p . nume ) ) ;
  }

  public int compareTo ( Object o ) {
    if ( o == null )
      throw new Null P o i n t e r E x c e p t i o n () ;
    if (!( o instanceof Persoana ) )
      throw new Clas sCas tExce ptio n ( " Nu pot compara ! " ) ;

      Persoana p = ( Persoana ) o ;
5.7. COMPARAREA OBIECTELOR                                                141

        return ( cod - p . cod ) ;
    }
}

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


5.7.2       Interfata Comparator
                   ¸
In cazul ˆ care dorim s˘ 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                                    a
solutie. Aceasta este oferit˘ tot de metoda sort din clasa java.util.Arrays,
     ¸                      a
dar ˆ varianta ˆ care, pe lˆng˘ vectorul ce trebuie sortat, vom transmite
     ın          ın            a a
un argument de tip Comparator care s˘ specifice modalitatea de comparare
                                         a
a elementelor.
    Interfata java.util.Comparator contine metoda compare, care impune
            ¸                              ¸
o ordine total˘ asupra elementelor unei colectii. Aceasta returneaz˘ un ˆ
              a                               ¸                    a    ıntreg
cu aceea¸i semnificatie ca la metoda compareTo a interfetei Comparator ¸i
          s          ¸                                      ¸                s
are 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                          a
Pentru definirea comparatorului vom folosi o clas˘ anonim˘.
                                                   a         a

            Listing 5.11: Sortarea unui vector folosind un comparator
import java . util .*;
class Sortare {
  public static void main ( String args []) {
    Persoana p [] = new Persoana [4];
    p [0] = new Persoana (3 , " Ionescu " ) ;
    p [1] = new Persoana (1 , " Vasilescu " ) ;
    p [2] = new Persoana (2 , " Georgescu " ) ;
    p [3] = new Persoana (4 , " Popescu " ) ;

        Arrays . sort (p , new Comparator () {
          public int compare ( Object o1 , Object o2 ) {
            Persoana p1 = ( Persoana ) o1 ;
            Persoana p2 = ( Persoana ) o2 ;
            return ( p1 . nume . compareTo ( p2 . nume ) ) ;
142                                           CAPITOLUL 5. INTERFETE
                                                                  ¸

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

   Observati cum compararea a dou˘ ¸iruri de caractere se face tot cu metoda
          ¸                      as
compareTo, clasa String implemenˆnd interfata Comparable.
                                 a           ¸


5.8        Adaptori
In cazul ˆ care o interfat˘ contine mai multe metode ¸i, la un moment
         ın                ¸a     ¸                        s
dat, avem nevoie de un obiect care implementeaz˘ interfata respectiv dar nu
                                                  a       ¸
specific˘ cod decˆt pentru o singur˘ metod˘, el trebui totu¸i s˘ implementeze
       a        a                   a      a               s a
toate 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() {}
5.8. ADAPTORI                                                             143

  });


    Aceast˘ abordare poate fi nepl˘cut˘ dac˘ avem frecvent nevoie de obiecte
           a                      a a      a
ale unor clase ce implementeaz˘ interfata X. Solutia la aceast˘ problem˘ este
                              a        ¸         ¸            a        a
folosirea adaptorilor.

    Definitie
          ¸
    Un adaptor este o clas˘ abstract˘ care implementeaz˘ o anumit˘ interfat˘
                          a         a                  a         a        ¸a
f˘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
            ¸   a
abstract˘, 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
144   CAPITOLUL 5. INTERFETE
                          ¸
Capitolul 6

Organizarea claselor

6.1     Pachete

Definitie
       ¸
   Un pachet este o colectie de clase ¸i interfete ˆ
                            ¸            s        ¸ ınrudite din punctul de
vedere al functionalit˘¸ii lor. Sunt folosite pentru g˘sirea ¸i utilizarea mai
              ¸       at                              a      s
u¸oar˘ a claselor, pentru a evita conflictele de nume ¸i pentru a controla
 s a                                                     s
accesul la anumite clase. In alte limbaje de programare pachetele se mai
numesc libr˘rii sau bibilioteci.
            a



6.1.1    Pachetele standard (J2SDK)
Platforma standard de lucru Java se bazeaz˘ pe o serie de pachete cu ajutorul
                                           a
c˘rora se pot construi ˆ
 a                      ıntr-o manier˘ simplificat˘ aplicatiile. Exist˘ deci un
                                     a           a       ¸           a
set de clase deja implementate care modeleaz˘ structuri de date, algoritmi sau
                                             a
diverse notiuni esentiale ˆ dezvoltarea unui program. Cele mai importante
           ¸         ¸    ın
pachete ¸i suportul oferit de lor sunt:
         s

   • java.lang - clasele de baz˘ ale limbajului Java
                               a

   • java.io - intr˘ri/ie¸iri, lucrul cu fi¸iere
                   a     s                s

   • java.util - clase ¸i interfete utile
                       s         ¸

   • java.applet - dezvoltarea de appleturi

                                      145
146                         CAPITOLUL 6. ORGANIZAREA CLASELOR

   • java.awt - interfata 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 pachet
Conform specificatiilor de acces ale unei clase ¸i ale mebrilor ei, doar clasele
                  ¸                             s
publice ¸i membrii declarati publici ai unei clase sunt accesibili ˆ afara pa-
        s                   ¸                                      ın
chetului ˆ 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
                                                       ın
pachet.
   Pentru a folosi o clas˘ public˘ dintr-un anumit pachet, sau pentru a apela
                         a       a
o metod˘ public˘ a unei clase publice a unui pachet, exist˘ trei solutii:
        a       a                                           a         ¸

   • specificarea numelui complet al clasei

   • importul clasei respective

   • importul ˆ
              ıntregului pachet ˆ care se g˘se¸te clasa.
                                ın         a s
6.1. PACHETE                                                                147

    Specificarea numelui complet al clasei se face prin prefixarea numelui scurt
al 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                 ın
acelei 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 a
s˘ 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 declararea
vreunei clase sau interfete.
                          ¸

6.1.3     Importul unei clase sau interfete
                                         ¸
Se face prin instructiunea import ˆ care specific˘m numele complet al clasei
                    ¸              ın             a
sau interfetei pe care dorim s˘ o folosim dintr-un anumit pacehet:
           ¸                  a

  import numePachet.numeClasa;

  //Pentru exemplul nostru:
  import java.awt.Button;
  import java.awt.TextField;

    Din acest moment, vom putea folosi ˆ clasele fi¸ierului ˆ care am plasat
                                       ın          s       ın
instructiunea 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");
148                        CAPITOLUL 6. ORGANIZAREA CLASELOR

   Aceast˘ abordare este eficient˘ ¸i recomandat˘ ˆ cazul ˆ care nu avem
          a                       as              a ın       ın
nevoie decˆt de cˆteva clase din pachetul respectiv. Dac˘ ˆ exemplul nostru
          a      a                                      a ın
am avea nevoie ¸i de clasele Line, Point, Rectangle, Polygon, ar trebui s˘
                s                                                         a
avem 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                                ıntregul
pachet ¸i nu al fiec˘rei clase ˆ parte.
       s            a         ın

6.1.4      Importul la cerere dintr-un pachet
Importul la cerere dintr-un anumit pachet se face printr-o instructiune import
                                                                   ¸
ˆ care specific˘m numele pachetului ale c˘rui clase ¸i interfete dorim s˘ le
ın              a                          a           s         ¸         a
folosim, 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       ın
instructiunea de import numele scurt al tuturor claselor pachetului importat:
       ¸

  Button b = new Button("OK");
  Point p = new Point(0, 0);



    Atentie
          ¸
    * nu are semnificatia uzual˘ de la fi¸iere de wildcard (masc˘) ¸i nu poate fi
                       ¸       a        s                     a s
folosit decˆt ca atare. O expresie de genul import java.awt.C*; va produce
           a
o eroare de compilare.
6.1. PACHETE                                                               149

    In cazul ˆ care sunt importate dou˘ sau mai multe pachete care contin
             ın                         a                                  ¸
clase (interfete) cu acela¸i nume, atunci referirea la ele trebuie f˘cut˘ doar
              ¸           s                                         a a
folosind numele complet, ˆ caz contrar fiind semnalat˘ o ambiguitate de
                            ın                             a
c˘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     a
pachete:
   • pachetul java.lang

        import java.lang.*;
        // Poate sau nu sa apara
        // Mai bine nu...

   • pachetul curent
   • pachetul implicit (f˘r˘ nume)
                         aa

6.1.5     Importul static
Aceast˘ facilitate, introdus˘ ˆ
        a                   a ıncepˆnd cu versiunea 1.5, permite referirea con-
                                    a
stantelor statice ale unei clase f˘r˘ a mai specifica numele complet al aces-
                                  aa
teia ¸i este implementat˘ prin ad˘ugarea cuvˆntului cheie static dup˘ cel de
     s                   a         a          a                        a
import:
                import static numePachet.NumeClasa.*;
Astfel, ˆ loc s˘ ne referim la constantele clasei cu expresii de tipul
        ın     a
NumeClasa.CONSTANTA, putem folosi doar numele constantei.
150                        CAPITOLUL 6. ORGANIZAREA CLASELOR

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

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



    Atentie
         ¸
    Importul static nu import˘ decˆt constantele statice ale unei clase, nu ¸i
                             a    a                                         s
clasa ˆ sine.
      ın




6.1.6    Crearea unui pachet
Toate clasele ¸i interfetele Java apartin la diverse pachete, grupate dup˘
               s         ¸                                                     a
functionalitatea lor. Dup˘ cum am v˘zut clasele de baz˘ se g˘sesc ˆ pa-
     ¸                      a            a                   a      a     ın
chetul java.lang, clasele pentru intr˘ri/ie¸iri sunt ˆ java.io, clasele pentru
                                       a    s        ın
interfata grafic˘ ˆ java.awt, etc.
        ¸       a ın
    Crearea unui pachet se realizeaz˘ prin scriere la ˆ
                                      a                ınceputul fi¸ierelor surs˘
                                                                  s            a
ce contin clasele ¸i interfetele pe care dorim s˘ le grup˘m ˆ
        ¸          s        ¸                    a         a ıntr-un pachet a
instructiunii: 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 {...}
6.1. PACHETE                                                                  151

  class ArboreBinar extends Arbore {...}


   Clasele Graf, GrafPerfect, Arbore, ArboreBinar vor face parte din
acela¸i pachet grafuri.
     s
   Instructiunea package actioneaz˘ asupra ˆ
          ¸                  ¸      a        ıntregului fi¸ier surs˘ la ˆ
                                                          s        a ınceputul
c˘ruia apare. Cu alte cuvinte nu putem specifica faptul c˘ anumite clase
 a                                                            a
dintr-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        a
parte din pachetul implicit (care nu are nici un nume). In general, pachetul
implicit este format din toate clasele ¸i intefetele directorului curent de lucru.
                                       s        ¸
Este recomandat ˆ a ca toate clasele ¸i intefetele s˘ fie plasate ˆ pachete,
                  ıns˘                    s              a            ın
pachetul implicit fiind folosit doar pentru aplicatii mici sau prototipuri.
                                                     ¸




6.1.7     Denumirea unui pachet

Exist˘ posibilitatea ca doi programatori care lucreaz˘ la un proiect comun
      a                                                   a
s˘ foloseasc˘ acela¸i nume pentru unele din clasele lor. De asemenea, se
 a           a     s
poate ca una din clasele unei aplicatii s˘ aib˘ acela¸i nume cu o clas˘ a
                                        ¸ a        a       s                 a
mediului Java. Acest lucru este posibil atˆt timp cˆt clasele cu acela¸i nume
                                             a         a                 s
se 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                                   a
comun 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 lor
Internet ˆ denumirea pachetelor implementate ˆ cadrul companiei, cum
         ın                                         ın
ar fi ro.companie.numePachet. In cadrul aceleiasi companii, conflictele de
nume 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                                     s
De exemplu, programatorul cu numele Ion al companiei XSoft, avˆnd contul
                                                                    a
ion@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                                                  ın
vor fi integrate.
152                         CAPITOLUL 6. ORGANIZAREA CLASELOR

6.2      Organizarea fi¸ierelor
                      s
6.2.1     Organizarea fi¸ierelor surs˘
                       s            a
Orice aplicatie nebanal˘ trebuie s˘ fie construit˘ folosind o organizare ier-
             ¸          a         a              a
arhic˘ a componentelor sale. Este recomandat ca strategia de organizare a
     a
fi¸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          a
printr-un exemplu concret. S˘ presupunem c˘ dorim crearea unor compo-
                                 a              a
nente care s˘ reprezinte diverse notiuni matematice din domenii diferite,
             a                         ¸
cum ar fi geometrie, algebr˘, analiz˘, etc. Pentru a simplifica lucrurile, s˘
                              a        a                                     a
presupunem c˘ dorim s˘ cre˘m clase care s˘ descrie urm˘toarele notiuni:
               a          a     a              a             a
poligon, 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
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                    s
Ierarhia 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                                    ın
mite corespunzator cu numele directoarelor ˆ care se gasesc:
                                            ın

// Poligon.java
package geometrie.plan;
public class Poligon { . . . }

// Cerc.java
package geometrie.plan;
public class Cerc { . . . }


// Poliedru.java
package geometrie.spatiu;
public class Poliedru { . . . }
154                         CAPITOLUL 6. ORGANIZAREA CLASELOR

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

// Grup.java
package algebra;
public class Grup { . . . }

// Functie.java
package analiza;
public class Functie { . . . }


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


6.2.2     Organizarea unit˘¸ilor de compilare (.class)
                          at
In urma compil˘rii fi¸ierelor surs˘ vor fi generate unit˘¸i de compilare pentru
                  a    s           a                     at
fiecare 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                    a
gaseasca ˆ ıntr-o ierarhie de directoare care s˘ reflecte numele pachetului din
                                                a
care face parte clasa respectiv˘.a
    Implicit, ˆ urma compil˘rii fi¸ierele surs˘ ¸i unit˘¸ile de compilare se
               ın               a      s          a s      at
g˘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
6.2. ORGANIZAREA FISIERELOR
                   ¸                                                         155

         Poliedru.class
         Sfera.class
     /algebra
       Grup.class
     /analiza
       Functie.class

     Matematica.class

Crearea acestei structuri ierarhice este facut˘ automat de c˘tre compilator.
                                              a             a
In directorul aplicatiei (matematica) cre˘m subdirectorul clase ¸i d˘m co-
                                           a                      s a
manda:

  javac -sourcepath surse surse/Matematica.java -d clase
sau
  javac -classpath surse surse/Matematica.java -d clase

    Optiunea -d specific˘ directorul r˘d˘cin˘ al ierarhiei de clase. In lipsa
       ¸                   a            a a a
lui, fiecare unitate de compilare va fi plasat˘ ˆ acela¸i director cu fi¸ierul s˘u
                                             a ın     s              s       a
surs˘.
     a
Deoarece compil˘m clasa principal˘ a plicatiei, vor fi compilate ˆ cascad˘
                  a                   a       ¸                     ın         a
toate clasele referite de aceasta, dar numai acestea. In cazul ˆ care dorim s˘
                                                               ın              a
compil˘m explicit toate fi¸ierele java dintr-un anumit director, de exemplu
       a                     s
surse/geometrie/plan, putem folosi expresia:

  javac surse/geometrie/plan/*.java -d clase

6.2.3     Necesitatea organiz˘rii fi¸ierelor
                             a     s
Organizarea fi¸ierelor surs˘ este necesar˘ deoarece ˆ momentul cˆnd compi-
                s            a             a           ın              a
latorul ˆ alneste un nume de clas˘ el trebuie s˘ poat˘ identifica acea clas˘,
        ıntˆ                          a            a      a                     a
ceea 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-
                  at
itatea interpretorului s˘ gaseasc˘ ¸i s˘ ˆ
                         a         a s a ıncarce ˆ memorie o anumit˘ clas˘ ˆ
                                                  ın                     a    a ın
timpul executiei programului.
              ¸
    Ins˘ aceast˘ organizare nu este suficient˘ deoarece specific˘ numai partea
       a        a                             a                    a
final˘ 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             a
la compilare cˆt ¸i la interpretare trebuie specificat˘ lista de directoare r˘d˘cin˘
               a s                                   a                      a a a
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            a
fi¸ierele necesare unei aplicatii. Fiecare director din calea de cautare este
  s                            ¸
directorul imediat superior structurii de directoare corespunz˘toare numelor
                                                                 a
pachetelor ˆ 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       a
Setarea 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
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 JAR
Fi¸ierele JAR (Java Archive) sunt arhive ˆ format ZIP folosite pentru
  s                                           ın
ˆ
ımpachetarea aplicatiilor Java. Ele pot fi folosite ¸i pentru comprim˘ri
                      ¸                                s                  a
obi¸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
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 jar
Arhivatorul jar se g˘se¸te ˆ subdirectorul bin al directorului ˆ care este
                    a s ın                                      ın
instalat kitul J2SDK. Mai jos sunt prezentate pe scurt operatiile uzuale:
                                                            ¸

   • Crearea unei arhive
     jar cf arhiva.jar 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 *
6.3. ARHIVE JAR                                                            159

6.3.2     Executarea aplicatiilor arhivate
                           ¸
Pentru a rula o aplicatie ˆ
                         ¸ ımpachetat˘ ˆ
                                       a ıntr-o arhiv˘ JAR trebuie s˘ facem
                                                     a                 a
cunoscut˘ interpretorului numele clasei principale a aplicatiei. S˘ consider˘m
         a                                                 ¸      a         a
urm˘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                    a
lansa comanda:

jar cvf mate.jar geometrie analiza algebra Matematica.class

    In urma acestei comenzi vom obtine arhiva mate.jar. Dac˘ vom ˆ
                                                                a       ıncerca
s˘ lans˘m ˆ executie aceast˘ arhiv˘ prin comanda java -jar mate.jar
 a      a ın        ¸          a       a
vom obtine urm˘toarea eroare: ”Failed to load Main-Class manifest from
         ¸      a
mate.jar”. Aceasta ˆ ınseamna c˘ ˆ fiserul Manifest.mf ce se gase¸te ˆ di-
                                 a ın                                 s ın
rectorul 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                         a
lansat˘ ˆ executie aplicatia ˆ
      a ın      ¸         ¸ ımpachetat˘ ˆ acea arhiv˘ (dac˘ exist˘ o clas˘
                                        a ın          a     a      a      a
principal˘).
         a
160   CAPITOLUL 6. ORGANIZAREA CLASELOR
Capitolul 7

Colectii
     ¸

7.1      Introducere
O colectie este un obiect care grupeaz˘ mai multe elemente ˆ
        ¸                                  a                        ıntr-o singur˘a
unitate. Prin intermediul colectiilor vom avea acces la diferite tipuri de
                                     ¸
date cum ar fi vectori, liste ˆ antuite, stive, multimi matematice, tabele de
                               ınl˘ ¸                 ¸
dispersie, etc. Colectiile sunt folosite atˆt pentru memorarea ¸i manipularea
                     ¸                     a                       s
datelor, 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˘ a
c˘ 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˘ a
unitar˘, 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                   ın
unei aplicatii. Cele mai importante sunt:
           ¸

                                       161
162                                           CAPITOLUL 7. COLECTII
                                                                ¸

   • Reducerea efortului de programare: prin punerea la dispozitia      ¸
     programatorului a unui set de tipuri de date ¸i algoritmi ce modeleaz˘
                                                     s                    a
     structuri ¸i operatii des folosite ˆ aplicatii.
               s       ¸                ın      ¸

   • 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            a
problemei ˆ sine ¸i nu asupra modului de reprezentare ¸i manipulare a
            ın      s                                     s
informatiilor.
        ¸


7.2     Interfete ce descriu colectii
               ¸                  ¸
Interfetele reprezint˘ nucleul mecanismului de lucru cu colectii, scopul lor
       ¸             a                                       ¸
fiind de a permite utilizarea structurilor de date independent de modul lor
de implementare.


    Collection modeleaz˘ o colectie la nivelul cel mai general, descriind un
                          a       ¸
grup de obiecte numite ¸i elementele sale. Unele implement˘ri ale acestei
                          s                                   a
interfete permit existenta elementelor duplicate, alte implement˘ri nu. Un-
       ¸                ¸                                       a
ele au elementele ordonate, altele nu. Platforma Java nu ofer˘ nici o im-
                                                                a
plementare direct˘ a acestei interfete, ci exist˘ doar implement˘ri ale unor
                  a                 ¸           a               a
subinterfete 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);
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     s
f˘r˘ a avea alte metode specifice.
 aa
Dou˘ dintre clasele standard care ofer˘ implement˘ri concrete ale acestei
     a                                 a            a
interfete 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                   a
Ordonarea elementelor se face conform ordinii lor naturale, sau conform cu
ordinea dat˘ de un comparator specificat la crearea colectiei ¸i este mentinut˘
            a                                             ¸ s            ¸   a
automat la orice operatie efectuat˘ asupra multimii. Singura condittie este
                        ¸           a              ¸                   ¸
ca, pentru orice dou˘ obiecte o1, o2 ale colectiei, apelul o1.compareT o(o2)
                      a                          ¸
(sau comparator.compare(o1, o2), dac˘ este folosit un comparator) trebuie
                                        a
s˘ fie valid ¸i s˘ nu provoace exceptii.
 a          s a                     ¸
    Fiind subclas˘ a interfetei Set, mo¸tene¸te metodele acesteia, oferind
                  a           ¸           s     s
metode suplimentare ce ¸in cont de faptul c˘ multimea este sortat˘:
                           t                  a      ¸               a
public interface SortedSet extends Set {

    // Subliste
    SortedSet subSet(Object fromElement, Object toElement);
    SortedSet headSet(Object toElement);
164                                          CAPITOLUL 7. COLECTII
                                                               ¸

    SortedSet tailSet(Object fromElement);

    // Capete
    Object first();
    Object last();

    Comparator comparator();
}

Clasa care implementeaz˘ aceast˘ interfat˘ este TreeSet.
                       a       a        ¸a


    List descrie liste (secvente) de elemente indexate. Listele pot contine
                              ¸
duplicate ¸i permit un control precis asupra pozitiei unui element prin in-
           s                                      ¸
termediul indexului acelui element. In plus, fat˘de metodele definite de
                                                 ¸a
interfata Collection, avem metode pentru acces pozitional, c˘utare ¸i it-
       ¸                                               ¸       a      s
erare 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);
}
7.2. INTERFETE CE DESCRIU COLECTII
            ¸                  ¸                                          165

Clase standard care implementeaz˘ aceast˘ interfat˘ sunt: ArrayList,
                                a       a        ¸a
LinkedList, Vector.


   Map descrie structuri de date ce asociaz˘ fiecarui element o cheie unic˘,
                                              a                             a
dup˘ 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                   a
Definittai interfetei este prezentat˘ mai jos:
       ¸         ¸                 a

public interface Map {
  // Metode cu caracter general
  int size();
  boolean isEmpty();
  void clear();

    // Operatii la nivel de element
    Object put(Object key, Object value);
    Object get(Object key);
    Object remove(Object key);
    boolean containsKey(Object key);
    boolean containsValue(Object value);

    // Operatii la nivel de multime
    void putAll(Map t);

    // Vizualizari ale colectiei
    public Set keySet();
    public Collection values();
    public Set entrySet();

    // Interfata pentru manipularea unei inregistrari
    public interface Entry {
      Object getKey();
      Object getValue();
      Object setValue(Object value);
    }
}
166                                             CAPITOLUL 7. COLECTII
                                                                  ¸

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


    SortedMap este asem˘n˘toare cu interfata Map, la care se adaug˘ fap-
                            a a                ¸                         a
tul c˘ multimea cheilor dintr-o astfel de colectie este mentinut˘ ordonat˘
     a     ¸                                     ¸            ¸     a         a
ascendent conform ordinii naturale, sau conform cu ordinea dat˘ de un com-
                                                                  a
parator specificat la crearea colectiei. Este subclasa a interfetei Map, oferind
                                  ¸                            ¸
metode suplimentare pentru: extragere de subtabele, aflarea primei/ultimei
chei, 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        ¸a


7.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                         s
au fost adaptate ˆ a¸a fel ˆ at s˘ se integreze ˆ noua abordare. Pe lˆng˘
                  ın s      ıncˆ a                ın                       a a
acestea 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
˘
7.3. IMPLEMENTARI ALE COLECTIILOR
                           ¸                                               167

    Clasele de baz˘ care implementeaz˘ interfete ce descriu colectii au numele
                   a                  a       ¸                  ¸
de forma < Implementare >< Interf ata >, unde ’implementare’ se refer˘ la  a
structura intern˘ folosit˘ pentru reprezentarea multimii, ¸i sunt prezentate
                 a       a                           ¸      s
ˆ tabelul de mai jos, ˆ
ın                      ımpreun˘ cu interfetele corespunz˘toare (clasele din
                                a          ¸               a
vechiul 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     ın
de mai jos:

AbstractCollection - AbstractSet, AbstractList - HashSet,
TreeSet... Vector-Stack
AbstractMap - HashMap, TreeMap, HashTable
In vechea ierarhie:
Dictionary - Hashtable - Properties

Evident, implementarea interfetelor este explicit realizat˘ la nivelul super-
                                  ¸                           a
claselor abstracte, acestea oferind de altfel ¸i implement˘ri concrete pentru
                                                s            a
multe din metodele definite de interfete. ¸
    In general, clasele care descriu colectii au unele tr˘saturi comune, cum ar
                                           ¸             a
fi:

   • permit elementul null,

   • sunt serializabile,

   • au definit˘ metoda clone,
              a
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                         ¸                        a
ArrayList ¸i LinkedList, prima fiind ˆ general mult mai folosit˘. De ce
               s                          ın                        a
exist˘ atunci ¸i clasa LinkedList ? Raspunsul const˘ ˆ faptul c˘ folosind
     a             s                                    a ın       a
reprezent˘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             s
necesar actualiz˘rii colectiei ˆ cazul unor modific˘ri.
                      a   ¸ ın                     a
    S˘ consider˘m un exemplu ce creeaza o list˘ folosind ArrayList, respectiv
     a              a                         a
LinkedList ¸i execut˘ diverse operatii pe ea, cronometrˆnd timpul necesar
                 s      a             ¸                    a
realiz˘rii acestora:
      a

              Listing 7.1: Comparare ArrayList - LinkedList
import java . util .*;

public class TestEficienta {

  final static int N = 100000;

  public static void testAdd ( List lst ) {
      long t1 = System . curre ntTimeM illis () ;
      for ( int i =0; i < N ; i ++)
        lst . add ( new Integer ( i ) ) ;
      long t2 = System . curre ntTimeM illis () ;
      System . out . println ( " Add : " + ( t2 - t1 ) ) ;
  }
˘
7.4. FOLOSIREA EFICIENTA A COLECTIILOR
                                ¸                                         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                               a
ArrayList ofer˘ acces ˆ timp constant la elementele sale ¸i din acest motiv
                a       ın                                   s
folosirea lui ”get” este rapid˘, ˆ timp ce pentru LinkedList este extrem
                               a ın
de lent˘, deoarece ˆ
        a           ıntr-o list˘ ˆ
                               a ınlantuit˘ accesul la un element se face prin
                                      ¸ a
170                                             CAPITOLUL 7. COLECTII
                                                                  ¸

parcurgerea secvential˘ a listei pˆn˘ la elementul respectiv.
                   ¸ a            a a
La operatiunea de eliminare, folosirea lui ArrayList este lent˘ deoarece el-
          ¸                                                       a
ementele r˘mase sufer˘ un proces de reindexare (shift la stˆnga), ˆ timp
            a          a                                        a      ın
ce pentru LinkedList este rapid˘ ¸i se face prin simpla schimbare a unei
                                   a s
leg˘turi. Deci, ArrayList se comport˘ bine pentru cazuri ˆ care avem
   a                                     a                        ın
nevoie de reg˘sirea unor elemente la pozitii diferite ˆ list˘, iar LinkedList
              a                            ¸          ın    a
functioneaza eficient atunci cˆnd facem multe operatii de modificare (¸tergeri,
     ¸                       a                      ¸                 s
inser˘ri).
      a


    Concluzia nu este c˘ una din aceste clase este mai ”bun˘” decˆt cealalt˘,
                          a                                  a    a          a
ci c˘ exist˘ diferente substantiale ˆ reprezentarea ¸i comportamentul diferitelor
    a      a        ¸         ¸     ın              s
implement˘ri ¸i c˘ alegerea unei anumite clase pentru reprezentarea unei
            a s a
multimi de elemente trebuie s˘ se fac˘ ˆ functie de natura problemei ce
    ¸                            a        a ın     ¸
trebuie rezolvat˘.a


7.5     Algoritmi polimorfici
Algoritmii polimorfici descri¸i ˆ aceast˘ sectiune sunt metode definite ˆ
                               s ın        a     ¸                          ın
clasa Collections care permit efectuarea unor operatii utile cum ar fi c˘utarea,
                                                       ¸                a
sortarea, etc. Caracterisiticile principale ale acestor algoritmi sunt:

   • sunt metode de clas˘ (statice);
                        a

   • au un singur argument de tip colectie;
                                       ¸

   • apelul lor general va 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
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 generice
Tipurile generice, introduse ˆ versiunea 1.5 a limbajului Java, simplific˘
                                ın                                             a
lucrul cu colectii, permitˆnd tipizarea elementelor acestora. Definirea unui
                ¸         ¸a
tip generic se realizeaz˘ prin specificarea ˆ
                        a                  ıntre paranteze unghiulare a unui tip
de date Java, efectul fiind impunerea tipului respectiv pentru toate elementele
colectiei: <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            ¸                          s
am dori ca elementele s˘ fie doar numere ˆ
                        a                 ıntregi. Mai mult, trebuie s˘ facem
                                                                      a
cast explicit de la tipul Object la Integer atunci cˆnd prelu˘m valoarea
                                                       a         a
unui element.
    Folosind tipuri generice, putem rescrie secventa astfel:
                                                   ¸
172                                             CAPITOLUL 7. COLECTII
                                                                  ¸

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


7.7      Iteratori ¸i enumer˘ri
                   s        a
Enumer˘rile ¸i iteratorii descriu modalit˘¸i pentru parcurgerea secvential˘ a
        a     s                           at                          ¸ a
unei colectii, indiferent dac˘ aceasta este indexat˘ sau nu. Ei sunt descri¸i
          ¸                  a                      a                      s
de obiecte ce implementeaz˘ interfetele Enumeration, respectiv Iterator
                             a       ¸
sau ListIterator. Toate clasele care implementeaz˘ colectii au metode ce
                                                       a     ¸
returneaz˘ o enumerare sau un iterator pentru parcurgerea elementelor lor.
          a
Deoarece functionalitatea interfetei Enumeration se reg˘se¸te ˆ Iterator,
                ¸                  ¸                      a s ın
aceasta din urm˘ este preferat˘ ˆ noile implement˘ri ale colectiilor.
                  a             a ın                 a           ¸
   Metodele uzuale ale acestor interfete sunt prezentate mai jos, ˆ
                                        ¸                          ımpreun˘ a
cu 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();
¸        ˘
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          ın
bele sensuri. Iteratorii sunt preferati enumer˘rilor datorit˘ posibilit˘¸ii lor
                                     ¸         a            a          at
de a actiona asupra colectiei pe care o parcurg prin metode de tip remove,
       ¸                  ¸
add, set dar ¸i prin faptul c˘ denumirile metodelor sunt mai concise.
                s              a


   Atentie¸
   Deoarece colectiile sunt construite peste tipul de date Object, metodele
                   ¸
de tip next sau prev ale iteratorilor vor returna tipul Object, fiind respon-
sabilitatea noastr˘ de a face conversie (cast) la alte tipuri de date, dac˘ este
                  a                                                       a
cazul.
174                                                CAPITOLUL 7. COLECTII
                                                                     ¸




    In exemplul de mai jos punem ˆ
                                 ıntr-un vector numerele de la 1 la 10, le
amestec˘m, dup˘ care le parcurgem element cu element folosind un iterator,
        a       a
ˆ
ınlocuind numerele pare cu 0.

                        Listing 7.2: Folosirea unui iterator
import java . util .*;
class TestIterator {
  public static void main ( String args []) {
    ArrayList a = new ArrayList () ;

        // Adaugam numerele de la 1 la 10
        for ( int i =1; i <=10; i ++)
          a . add ( new Integer ( i ) ) ;

        // Amestecam elementele colectiei
        Collections . shuffle ( a ) ;
        System . out . println ( " Vectorul amestecat : " + a ) ;

        // Parcurgem vectorul
        for ( ListIterator it = a . listIterator () ; it . hasNext () ; ) {
          Integer x = ( Integer ) it . next () ;

          // Daca elementul curent este par , il facem 0
          if ( x . intValue () % 2 == 0)
            it . set ( new Integer (0) ) ;
        }
        System . out . print ( " Rezultat : " + a ) ;
    }
}




   Incepˆnd cu versiunea 1.5 a limbajului Java, exist˘ o variant˘ simplificat˘
          a                                            a        a           a
de utilizare a iteratorilor. Astfel, o secvent˘ de genul:
                                             ¸a

    ArrayList<Integer> list = new ArrayList<Integer>();
    for (Iterator i = list.iterator(); i.hasNext();) {
      Integer val=(Integer)i.next();
      // Proceseaza val
      ...
¸        ˘
7.7. ITERATORI SI ENUMERARI                            175

 }

  poate fi rescris˘ astfel:
                 a

 ArrayList<Integer> list = new ArrayList<Integer>();
 for (Integer val : list) {
   // Proceseaza val
   ...
 }
176   CAPITOLUL 7. COLECTII
                        ¸
Capitolul 8

Serializarea obiectelor

8.1     Folosirea serializ˘rii
                          a

Definitie¸
    Serializarea este o metod˘ ce permite transformarea unui obiect ˆ
                             a                                          ıntr-o
secventa de octeti sau caractere din care s˘ poat˘ fi ref˘cut ulterior obiectul
       ¸˘        ¸                         a     a      a
original.

    Cu alte cuvinte, serializarea permite salvarea ˆ ıntr-o manier˘ unitar˘ a
                                                                  a       a
tuturor informatiilor unui obiect pe un mediu de stocare extern programului.
                 ¸
Procesul invers, de citire a unui obiect serializat pentru a-i reface starea
original˘, se nume¸te deserializare. Intr-un cadru mai larg, prin serializare
        a           s
se ˆ
   ı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
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            a
obiect 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
                                                    a
de obiectul care se serializeaz˘ pot referi la rˆndul lor alte obiecte, ¸i a¸a mai
                                a               a                       s s
departe. 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                                         a
unui obiect nu este tocmai facil.
    In cazul ˆ care starea unui obiect este format˘ doar din valori ale unor
              ın                                      a
variabile de tip primitiv, atunci salvarea informatiilor ˆ
                                                       ¸      ıncapsulate ˆ acel
                                                                            ın
obiect se poate face ¸i prin salvarea pe rˆnd a datelor, folosind clasa
                      s                     a
DataOutputStream, pentru ca apoi s˘ fie restaurate prin metode ale clasei
                                         a
DataInputStream, dar, a¸a cum am vazut, o asemenea abordare nu este
                             s
˘
8.1. FOLOSIREA SERIALIZARII                                                179

ˆ general suficient˘, deoarece pot ap˘rea probleme cum ar fi: variabilele
ın                   a                   a
membre ale obiectului pot fi instante ale altor obiecte, unele cˆmpuri pot
                                       ¸                       a
face referint˘ la acela¸i obiect, etc.
            ¸a         s


    Serializarea ˆ format binar a tipurilor primitive ¸i a obiectelor se real-
                 ın                                    s
izeaz˘ prin intermediul fluxurilor definite de clase specializate ˆ acest scop
      a                                                          ın
cu ar fi:
ObjectOutputStream pentru scriere ¸i ObjectInputStream pentru restau-
                                      s
rare.
    In continuare, prin termenul serializare ne vom referi doar la serializarea
ˆ format binar.
ın

8.1.1     Serializarea tipurilor primitive
Serializarea tipurilor primitive poate fi realizat˘ fie prin intermediul fluxu-
                                                  a
rilor 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
                               a
writeTipPrimitiv pentru scrierea/citirea datelor primitive ¸i a ¸irurilor de
                                                                s    s
caractere.
    Mai jos este prezentat un exemplu de serializare folosind clasa DataOutputStream:

  FileOutputStream fos = new FileOutputStream("test.dat");
  DataOutputStream out = new DataOutputStream(fos);
  out.writeInt(12345);
  out.writeDouble(12.345);
  out.writeBoolean(true);
  out.writeUTF("Sir de caractere");
  out.flush();
  fos.close();

   Citirea informatiilor scrise ˆ exemplul de mai sus se va face astfel:
                  ¸             ın

  FileInputStream fis = new FileInputStream("test.dat");
  DataInputStream in = new DataInputStream(fis);
  int i = in.readInt();
  double d = in.readDouble();
  boolean b = in.readBoolean();
180                     CAPITOLUL 8. SERIALIZAREA OBIECTELOR

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


8.1.2    Serializarea obiectelor
Serializarea obiectelor se realizeaz˘ prin intermediul fluxurilor definite de
                                       a
clasele ObjectOutputStream (pentru salvare) ¸i ObjectInputStream
                                                        s
(pentru restaurare). Acestea sunt fluxuri de procesare, ceea ce ˆ         ınseamna
c˘ vor fi folosite ˆ
 a                 ımpreuna cu alte fluxuri pentru scrierea/citirea efectiv˘ a  a
datelor pe mediul extern pe care va fi salvat, sau de pe care va fi restaurat
un obiect serializat.
    Mecanismul implicit de serializare a unui obiect va salva numele clasei
obiectului, signatura clasei ¸i valorile tuturor cˆmpurile serializabile ale obiec-
                             s                    a
tului. Referintele la alte obiecte serializabile din cadrul obiectului curent vor
               ¸
duce automat la serializarea acestora iar referintele multiple c˘tre un acela¸i
                                                    ¸              a             s
obiect 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                                a
exista ¸i metode pentru scrierea/citirea datelor primitive ¸i a ¸irurilor de
        s                                                        s    s
caractere.
    Metodele pentru serializarea obiectelor sunt:

   • writeObject, pentru scriere ¸i
                                 s

   • readObject, pentru restaurare.


8.1.3    Clasa ObjectOutputStream
Scrierea obiectelor pe un flux de ie¸ire este un proces extrem de simplu,
                                     s
secventa uzual˘ fiind cea de mai jos:
       ¸      a

  ObjectOutputStream out = new ObjectOutputStream(fluxPrimitiv);
  out.writeObject(referintaObiect);
  out.flush();
  fluxPrimitiv.close();
˘
8.1. FOLOSIREA SERIALIZARII                                                 181

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

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

  TipReferinta ref = (TipReferinta)in.readObject();
  fluxPrimitiv.close();

   Citirea informatiilor scrise ˆ exemplul de mai sus se va face astfel:
                  ¸             ın

  FileInputStream fis = new FileInputStream("test.ser");
  ObjectInputStream in = new ObjectInputStream(fis);
  String mesaj = (String)in.readObject();
  Date data = (Date)in.readObject();
  fis.close();

   Trebuie observat c˘ metoda readObject are tipul returnat Object, ceea
                     a
ce ˆ
   ınseamn˘ c˘ trebuie realizat˘ explicit conversia la tipul corespunzator
            a a                a
obiectului citit:

  Date date = in.readObject(); // gresit
  Date date = (Date)in.readObject(); // corect




    Atentie¸
    Ca ¸i la celelalte 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        ın
acestea au fost scrise, altfel vor ap˘rea evident exceptii ˆ procesul de dese-
                                     a                 ¸ ın
rializare.


   Clasa ObjectInputStream implementeaz˘ interfata DataInput deci, pe
                                             a        ¸
lˆng˘ metoda de citire a obiectelor, clasa pune la dispozitie ¸i metode de
 a a                                                       ¸ s
tipul readTipPrimitiv pentru citirea tipurilor de date primitive ¸i a ¸irurilor
                                                                 s s
de caractere.

  int i = in.readInt();
  double d = in.readDouble();
  boolean b = in.readBoolean();
  String s = in.readUTF();
8.2. OBIECTE SERIALIZABILE                                                    183

8.2      Obiecte serializabile
Un obiect este serializabil dac˘ ¸i numai dac˘ clasa din care face parte im-
                                as              a
plementeaz˘ 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          a
sau indirect, interfata Serializable.
                     ¸

8.2.1     Implementarea interfetei Serializable
                               ¸
Interfata Serializable nu contine nici o declaratie de metod˘ sau constant˘,
       ¸                        ¸                   ¸           a               a
singurul ei scop fiind de a identifica clasele ale c˘ror obiecte sunt serializabile.
                                                  a
Definitia sa complet˘ este:
      ¸              a
package java.io;
public interface Serializable {
  // Nimic !
}
   Declararea claselor ale c˘ror instante trebuie s˘ fie serializate este a¸adar
                            a          ¸           a                      s
extrem 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          a
sa 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.
           ¸
184                      CAPITOLUL 8. SERIALIZAREA OBIECTELOR

    In procesul serializ˘rii, dac˘ este ˆ alnit un obiect care nu implementeaz˘
                        a        a      ıntˆ                                  a
interfata Serializable atunci va fi generat˘ o exceptie de tipul NotSerializableException
       ¸                                      a        ¸
ce va identifica respectiva clas˘ neserializabil˘.
                                 a               a

8.2.2     Controlul serializ˘rii
                            a
Exist˘ cazuri cˆnd dorim ca unele variabile membre ale unui obiect s˘ nu fie
      a         a                                                      a
salvate automat ˆ procesul de serializare. Acestea sunt cazuri comune atunci
                  ın
cˆ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       a
private ˆ cadrul clasei aceste cˆmpuri particip˘ la serializare. Pentru ca un
         ın                     a                 a
cˆmp s˘ nu fie salvat ˆ procesul de serializare el trebuie declarat cu modi-
 a      a                ın
ficatorul transient ¸i trebuie s˘ fie ne-static. De exemplu, declararea unei
                       s         a
variabile membre temporare ar trebui facut˘ astfel:
                                              a
    transient private double temp;
    // Ignorata la serializare
    Modificatorul static anuleaz˘ efectul modificatorului transient. Cu
                                    a
alte 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                              a
cele marcate ’NU’, nu particip˘ iar cele marcate cu ’Exceptie’ vor provoca
                              a
exceptii de tipul NotSerializableException.
      ¸

                 Listing 8.1: Modificatorii static ¸i transient
                                                  s
import 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 ;
    }
}
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 neserializabili
import java . io .*;

class A {
  int x =1;
}

class B implements Serializable {
  int y =2;
}

public class Test2 implements Serializable {
  A a = new A () ; // Exceptie
  B b = new B () ; // DA

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


    Atunci cˆnd o clas˘ serializabila deriva dintr-o alt˘ clas˘, salvarea cˆmpurilor
             a         a                                a     a            a
clasei p˘rinte se va face doar dac˘ ¸i aceasta este serializabil˘. In caz contrar,
        a                         as                            a
subclasa trebuie s˘ salveze explicit ¸i cˆmpurile mo¸tenite.
                   a                  s a               s

                Listing 8.3: Serializarea cˆmpurilor mo¸tenite
                                           a           s
import java . io .*;
class C {
  int x =0;
  // Obligatoriu constructor fara argumente
}

class D extends C implements Serializable {
  int y =0;
}

public class Test3 extends D {
  public Test3 () {
    x = 1; // NU
    y = 2; // DA
186                         CAPITOLUL 8. SERIALIZAREA OBIECTELOR

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

   Mai jos este descrisa o aplicatie care efectueaz˘ salvarea ¸i restaurarea
                                   ¸                 a        s
unor obiecte din cele trei clase prezentate mai sus.

                          Listing 8.4: Testarea serializ˘rii
                                                        a
import java . io .*;

public class Exemplu {

    public static void test ( Object obj ) throws IOException {
      // Salvam
      FileOutputStream fos = new FileOutputStream ( " fisier . ser " )
         ;
      ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ;

        out . writeObject ( obj ) ;
        out . flush () ;

        fos . close () ;
        System . out . println ( " A fost salvat obiectul : " + obj ) ;

        // Restauram
        FileInputStream fis = new FileInputStream ( " fisier . ser " ) ;
        ObjectInputStream in = new Obje ctInput Stream ( fis ) ;
        try {
          obj = in . readObject () ;
        } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) {
          e . printStackTrace () ;
        }

        fis . close () ;
        System . out . println ( " A fost restaurat obiectul : " + obj ) ;
    }

    public static void main ( String args []) throws IOException {
      test ( new Test1 () ) ;
      try {
        test ( new Test2 () ) ;
      } catch ( N o t Se r i a l i z a b l e E x c e p t i o n e ) {
˘
8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR                                  187

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

        test ( new Test3 () ) ;
    }
}

    Rezultatul acestui program va fi :

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


8.3         Personalizarea serializ˘rii obiectelor
                                   a
Dezavantajul mecanismului implicit de serializare este c˘ algoritmul pe care
                                                           a
se beazeaz˘, fiind creat pentru cazul general, se poate comporta ineficient ˆ
           a                                                                   ın
anumite situatii: poate fi mult mai lent decˆt este cazul sau reprezentarea
               ¸                                a
binar˘ generat˘ pentru un obiect poate fi mult mai voluminoas˘ decˆt ar
      a         a                                                     a    a
trebui. In aceste situatii, putem s˘ ˆ
                         ¸             a ınlocuim algoritmul implicit cu unul
propriu, particularizat pentru o clas˘ anume. De asemenea, este posibil
                                         a
s˘ extindem comportamentul implicit, 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˘      a
am 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-
                                                                        a
izabil˘!) a metodelor writeObject ¸i readObject avˆnd exact signatura de
      a                               s                a
mai jos:

    private void writeObject(java.io.ObjectOutputStream stream)
       throws IOException
    private void readObject(java.io.ObjectInputStream stream)
       throws IOException, ClassNotFoundException

    Metoda writeObject controleaz˘ ce date sunt salvate iar readObject
                                     a
controleaz˘ modul ˆ care sunt restaurate obiectele, citind informatiile salvate
           a       ın                                               ¸
¸i, eventual, modifcˆnd starea obiectelor citite astfel ˆ at ele s˘ corespund˘
s                   a                                   ıncˆ      a           a
188                      CAPITOLUL 8. SERIALIZAREA OBIECTELOR

anumitor cerinte. In cazul ˆ care nu dorim s˘ ˆ
              ¸            ın                a ınlocuim complet mecanis-
mul standard, putem s˘ folosim metodele defaultWriteObject, respectiv
                       a
defaultReadObject care descriu procedurile implicite de serializare.
    Forma general˘ de implementare a metodelor writeObject ¸i readObject
                 a                                         s
este:
private void writeObject(ObjectOutputStream stream)
    throws IOException {

    // Procesarea campurilor clasei (criptare, etc.)
    ...
    // Scrierea obiectului curent
    stream.defaultWriteObject();

    // Adaugarea altor informatii suplimentare
    ...
}

private void readObject(ObjectInputStream stream)
    throws IOException,ClassNotFoundException {

    // Restaurarea obiectului curent
    stream.defaultReadObject();

    // Actualizarea starii obiectului (decriptare, etc.)
    // si extragerea informatiilor suplimentare
    ...
}
    Metodele writeObject ¸i readObject sunt responsabile cu serializarea
                             s
clasei ˆ care sunt 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             s
prie cu serializarea superclasei sale, atunci trebuie s˘ implementeze interfata
                                                       a                     ¸
Externalizable.

8.3.1     Controlul versiunilor claselor
S˘ 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
˘
8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR                               189

notiunea de angjat. O variant˘ simplificat˘ a acesteia ar putea ar˘ta astfel:
  ¸                          a           a                       a

                Listing 8.5: Prima variant˘ a clasei Angajat
                                          a
import java . io .*;

class Angajat implements Serializable {

    public String nume ;
    public int salariu ;
    private String parola ;

    public Angajat ( String nume , int salariu , String parola ) {
      this . nume = nume ;
      this . salariu = salariu ;
      this . parola = parola ;
    }

    public String toString () {
      return nume + " ( " + salariu + " ) " ;
    }

}


    Mai jos este prezentat˘ o mic˘ aplicatie care permite introducerea de
                             a        a     ¸
angajati ¸i salvarea lor ˆ
        ¸ s               ıntr-un 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          ¸a
angajatilor cu noi persoane.
        ¸

              Listing 8.6: Aplicatia de gestionare a angajatilor
                                 ¸                         ¸
import java . io .*;
import java . util .*;

public class GestiuneAngajati {

    // Lista angajatilor
    ArrayList ang = new ArrayList () ;

    public void citire () throws IOException {
      FileInputStream fis = null ;
      try {
        fis = new FileInputStream ( " angajati . ser " ) ;
        ObjectInputStream in = new Obje ctInput Stream ( fis ) ;
190                       CAPITOLUL 8. SERIALIZAREA OBIECTELOR

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

  public void salvare () throws IOException {
    FileOutputStream fos =
      new FileOutputStream ( " angajati . ser " ) ;
    ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ;
    out . writeObject ( ang ) ;
  }

  public void adaugare () throws IOException {
     BufferedReader stdin = new BufferedReader (
       new InputStream Reader ( System . in ) ) ;

       while ( true ) {
         System . out . print ( "  nNume : " ) ;
         String nume = stdin . readLine () ;

           System . out . print ( " Salariu : " ) ;
           int salariu = Integer . parseInt ( stdin . readLine () ) ;

           System . out . print ( " Parola : " ) ;
           String parola = stdin . readLine () ;

           ang . add ( new Angajat ( nume , salariu , parola ) ) ;

           System . out . print ( " Mai adaugati ? ( D / N ) " ) ;
           String raspuns = stdin . readLine () . toUpperCase () ;
           if ( raspuns . startsWith ( " N " ) )
             break ;
       }
  }

  public static void main ( String args []) throws IOException {
    GestiuneAngajati app = new GestiuneAngajati () ;
˘
8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR                                  191


        // Incarcam angajatii din fisier
        app . citire () ;

        // Adaugam noi angajati
        app . adaugare () ;

        // Salvam angajatii inapoi fisier
        app . salvare () ;
    }
}


    Problema care se pune acum este urm˘toarea. Dup˘ introducerea unui
                                                a            a
num˘r suficient de mare de angajati ˆ fi¸ier, clasa Angajat este modifi-
      a                                  ¸ ın s
cat˘ 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
       ¸            ¸                                          ¸         s
va 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             a
tip transient. De ce se ˆ amplˆ acest lucru ?
                            ıntˆ     a
    Explicatia const˘ ˆ faptul c˘ mecanismul de serializare Java este foarte
              ¸        a ın         a
atent cu signatura claselor serializate. Pentru fiecare obiect serializat este
calculat 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                                    a
este 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˘   a
cu celelalte date. In plus, orice modificare semnificativ˘ a clasei, cum ar
                                                              a
fi ad˘ugarea unui nou cˆmp, va determina modificarea num˘rului s˘u de
      a                     a                                    a         a
versiune.
    La restaurarea unui obiect, num˘rul de versiune salvat ˆ forma serializat˘
                                       a                      ın                a
va fi reg˘sit ¸i comparat cu noua semn˘tur˘ a clasei obiectului. In cazul ˆ
           a s                              a a                                ın
care 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               a
unor anomalii ce pot ap˘rea cˆnd dou˘ versiuni de clase sunt incompati-
                            a      a         a
bile, dat poate fi sup˘r˘toare atunci cˆnd modific˘rile aduse clasei nu stric˘
                        aa                a          a                          a
compatibilitatea cu vechea versiune. In aceast˘ situatie trebuie s˘ comu-
                                                   a       ¸           a
nic˘m explicit c˘ cele dou˘ clase sunt compatibile. Acest lucru se realizeaz˘
   a                 a       a                                                  a
prin setarea manual˘ a variabilei serialVersionUID ˆ cadrul clasei dorite,
                       a                                 ın
ad˘ugˆnd pur ¸i simplu cˆmpul:
   a a             s        a
192                      CAPITOLUL 8. SERIALIZAREA OBIECTELOR

static final long serialVersionUID = /* numar_serial_clasa */;
    Prezenta variabilei serialVersionUID printre membrii unei clase va in-
             ¸
forma algoritmul de serialzare c˘ nu mai calculeze num˘rul de serie al clasei,
                                  a                        a
ci s˘-l foloseasc˘ pe cel specificat de noi. Cum putem afla ˆ a num˘rul de se-
    a            a                                           ıns˘      a
rie al vechii clase Angajat care a fost folosit˘ anterior la salvarea angajatilor
                                               a                            ¸
?
    Utilitarul serialVer permite generarea num˘rului serialVersionUID pen-
                                                  a
tru 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       a
Angajat:
static final long serialVersionUID = 5653493248680665297L;
   Vom rescrie noua clas˘ Angajat astfel ˆ at s˘ fie compatibil˘ cu cea
                        a                ıncˆ a               a
veche astfel:

             Listing 8.7: Variant˘ compatibil˘ a clasei Angajat
                                 a           a
import java . io .*;

class Angajat implements Serializable {

    static final long serialVersionUID = 56 534 93 248 680 665 29 7 L ;

    public String nume , adresa ;
    public int salariu ;
    private String parola ;

    public Angajat ( String nume , int salariu , String parola ) {
      this . nume = nume ;
      this . adresa = " Iasi " ;
      this . salariu = salariu ;
      this . parola = parola ;
    }

    public String toString () {
      return nume + " ( " + salariu + " ) " ;
    }

}

   Aplicatia noastr˘ va functiona acum, ˆ a rubrica adres˘ nu va fi initializat˘
          ¸         a        ¸            ıns˘            a            ¸      a
ˆ nici un fel (va fi null), deoarece ea nu exista ˆ formatul original. La noua
ın                                               ın
˘
8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR                                 193

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

8.3.2     Securizarea datelor
Dup˘ cum am 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 ın
binar, informatiile unui obiect serializat nu sunt criptate ˆ nici un fel ¸i pot
                ¸                                           ın            s
fi reg˘site cu u¸urint˘, ceea ce poate reprezenta un inconvenient atunci cˆnd
      a         s     ¸a                                                     a
exist˘ cˆmpuri confidentiale.
      a a                ¸
    Rezolvarea acestei probleme se face prin modificarea mecanismului im-
plicit de serializare, implementˆnd metodele readObject ¸i writeObject,
                                a                              s
precum ¸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
                                             a
import java . io .*;

class Angajat implements Serializable {

  static final long serialVersionUID = 5 65 349 324 868 06 652 97 L ;

  public String nume , adresa ;
  public int salariu ;
  private String parola ;

  public Angajat ( String nume , int salariu , String parola ) {
    this . nume = nume ;
    this . adresa = " Iasi " ;
    this . salariu = salariu ;
    this . parola = parola ;
  }

  public String toString () {
    return nume + " ( " + salariu + " ) " ;
  }

  static String criptare ( String input , int offset ) {
    StringBuffer sb = new StringBuffer () ;
    for ( int n =0; n < input . length () ; n ++)
      sb . append (( char ) ( offset + input . charAt ( n ) ) ) ;
    return sb . toString () ;
194                          CAPITOLUL 8. SERIALIZAREA OBIECTELOR

    }

    private void writeObject ( Ob je ct Ou tpu tS tr ea m stream )
        throws IOException {
      parola = criptare ( parola , 3) ;
      stream . defaultWri te Ob je ct () ;
      parola = criptare ( parola , -3) ;
    }

    private void readObject ( O bjectIn putStre am stream )
        throws IOException , C l a s s N o t F o u n d E x c e p t i o n {
      stream . defaultRead Object () ;
      parola = criptare ( parola , -3) ;
    }
}




8.3.3       Implementarea interfetei Externalizable
                                 ¸
Pentru un control complet, explicit, al procesului de serializare, o clas˘ tre-
                                                                         a
buie s˘ implementeze interfata Externalizable. Pentru instante ale acestor
      a                      ¸                                   ¸
clase doar numele clasei este salvat automat pe fluxul de obiecte, clasa fiind
responsabil˘ cu scrierea ¸i citirea membrilor s˘i ¸i trebuie s˘ se coordoneze
            a             s                    a s            a
cu 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                           a
External ¸i readExternal ˆ care se va face serializarea complet˘ a obiectelor
             s               ın                                  a
¸i coordonarea cu superclasa ei.
s
    Uzual, interfata Externalizable este folosit˘ ˆ situatii ˆ care se dore¸te
                  ¸                             a ın     ¸ ın              s
ˆ
ımbun˘t˘¸irea performantelor algoritmului standard, mai exact cre¸terea vitezei
        a at               ¸                                       s
procesului de serializare.
˘
8.3. PERSONALIZAREA SERIALIZARII OBIECTELOR                            195

    Mai jos este prezentat˘ o clas˘ simpl˘ ¸i modalitatea de rescriere a sa
                          a       a      a s
folosind interfata Externalizable:
                ¸

                       Listing 8.9: Serializare implicit˘
                                                        a
import java . io .*;

class Persoana implements Serializable {
  int cod ;
  String nume ;

    public Persoana ( String nume , int cod ) {
      this . nume = nume ;
      this . cod = cod ;
    }
}



                       Listing 8.10: Serializare proprie
import java . io .*;

class Persoana implements Externalizable {
  int cod ;
  String nume ;

    public Persoana ( String nume , int cod ) {
      this . nume = nume ;
      this . cod = cod ;
    }

    public void writeExternal ( ObjectOutput s )
          throws IOException {
      s . writeUTF ( nume ) ;
      s . writeInt ( cod ) ;
    }

    public void readExternal ( ObjectInput s )
        throws ClassNotFoundException , IOException {
      nume = s . readUTF () ;
      cod = s . readInt () ;
    }

}
196                       CAPITOLUL 8. SERIALIZAREA OBIECTELOR

8.4      Clonarea obiectelor
Se ¸tie c˘ nu putem copia valoarea unui obiect prin instructiunea de atribuire.
   s     a                                                 ¸
O secventa de forma:
          ¸
  TipReferinta o1 = new TipReferinta();
  TipReferinta o2 = o1;
nu face 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 clone
definit˘ ˆ clasa Object. Aceasta creeaz˘ un nou obiect ¸i initializeaz˘ toate
       a ın                               a                 s    ¸      a
variabilele sale membre cu valorile obiectului clonat.
  TipReferinta o1 = new TipReferinta();
  TipReferinta o2 = (TipReferinta) o1.clone();
    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 a
referinta 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               ımpreuna
cu copierea tuturor obiectelor referite de cˆmpurile acelui obiect poate fi
                                                a
implementat˘ prin mecanismul serializ˘rii astfel:
               a                          a
public Object clone() {
  try {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(baos);
    out.writeObject(this);
    out.close();

      byte[] buffer = baos.toByteArray();
      ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
      ObjectInputStream in = new ObjectInputStream(bais);
      Object ret = in.readObject();
      in.close();

      return ret;

  } catch (Exception e) {
8.4. CLONAREA OBIECTELOR         197

        System.out.println(e);
        return null;
    }
}
198   CAPITOLUL 8. SERIALIZAREA OBIECTELOR
Capitolul 9

Interfata grafic˘ cu utilizatorul
       ¸       a

9.1      Introducere
Interfata grafic˘ cu utilizatorul (GUI), este un termen cu ˆ ¸eles larg care
        ¸        a                                              ınt
se refer˘ la toate tipurile de comunicare vizual˘ ˆ
         a                                        a ıntre un program ¸i utiliza-
                                                                        s
torii s˘i. Aceasta este o particularizare a interfetei cu utilizatorul (UI), prin
       a                                           ¸
care vom ˆ ıntelege conceptul generic de interactiune dintre program ¸i utiliza-
                                                ¸                       s
tori. Limbajul Java pune la dispozitie numeroase clase pentru implementarea
                                      ¸
diverselor functionalitati UI, ˆ a ne vom ocupa ˆ continuare de cele care
                 ¸              ıns˘                  ın
permit realizarea intefetei grafice cu utilizatorul (GUI).
                         ¸
    De la aparitia limbajului Java, bibliotecile de clase care ofer˘ servicii
                  ¸                                                     a
grafice au suferit probabil cele mai mari schimb˘ri ˆ trecerea de la o ver-
                                                    a ın
siune la alta. Acest lucru se datoreaz˘, pe de o parte dificult˘¸ii legate de
                                         a                          at
implementarea notiunii de portabilitate, pe de alt˘ parte nevoii de a integra
                    ¸                                 a
mecanismele GUI cu tehnologii ap˘rute ¸i dezvoltate ulterior, cum ar fi Java
                                     a     s
Beans. 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
200                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

    A¸adar, este de preferat ca aplicatiile Java s˘ fie create folosind tehnologia
      s                                ¸          a
Swing, 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
                                                    as
evenimentelor din AWT, deoarece va fi simplificat procesul de ˆ ¸elegere a
                                                                      ınt
dezvolt˘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.
            s


9.2      Modelul AWT
Pachetul care ofer˘ componente AWT este java.awt.
                    a
Obiectele grafice sunt derivate din Component, cu exceptia meniurilor care
                                                             ¸
descind din clasa MenuComponent. A¸adar, prin notiunea de component˘
                                            s              ¸                  a
vom ˆ ıntelege ˆ continuare orice obiect care poate avea o reprezentare grafic˘
               ın                                                             a
¸i care poate interactiona cu utilizatorul. Exemple de componente sunt fere-
s
strele, butoanele, listele, bare de defilare, etc. Toate componentele AWT sunt
definte de clase proprii ce se gasesc ˆ pachetul java.awt, clasa Component
                                        ın
fiind superclasa abstract˘ a tuturor acestor clase.
                            a
    Crearea obiectelor grafice nu realizeaz˘ automat ¸i afi¸area lor pe ecran.
                                              a          s   s
Mai ˆ ai ele trebuie a¸ezate pe o suprafata de afi¸are, care poate fi o fereastr˘
     ıntˆ               s                           s                         a
9.2. MODELUL AWT                                                           201

sau un applet, ¸i vor deveni vizibile ˆ momentul ˆ care suprafata pe care sunt
                  s                   ın             ın              ¸
afi¸ate va fi vizibil˘. O astfel de suprafat˘ pe care sunt plasate componente
   s                  a                     ¸a
se mai nume¸te container ¸i reprezint˘ o instant˘ a unei clase derivate din
                s            s            a           ¸a
Container. Clasa Container este o subclas˘ aparte a lui Component, fiind
                                                  a
la 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        ın
momentul ˆ care utilizatorul a efectuat o actiune ¸i, prin urmare, compo-
             ın                                    ¸     s
nentele 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                        a
siunea 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. In
schimb, ˆ ıntr-un program trebuie specificat codul care se execut˘ la aparitia
                                                                       a    ¸
unui eveniment. Tratarea evenimentelor se realizeaz˘ prin intermediul unor
                                                          a
clase de tip listener (ascult˘tor, consumator de evenimente), clase care sunt
                             a
definite ˆ pachetul java.awt.event. In Java, orice component˘ poate ”con-
         ın                                                           a
suma” evenimentele generate de o alt˘ component˘ (vezi ”Tratarea eveni-
                                          a              a
mentelor”).
     S˘ consider˘m un mic exemplu, ˆ care cre˘m o fereastr˘ ce contine dou˘
       a          a                    ın          a              a       ¸   a
butoane.

                  Listing 9.1: O fereastr˘ cu dou˘ butoane
                                         a       a
import java . awt .*;
public class ExempluAWT1 {
  public static void main ( String args []) {

     // Crearea ferestrei - un obiect de tip Frame
     Frame f = new Frame ( " O fereastra " ) ;

     // Setarea modului de dipunere a componentelor
     f . setLayout ( new FlowLayout () ) ;

     // Crearea celor doua butoane
     Button b1 = new Button ( " OK " ) ;
     Button b2 = new Button ( " Cancel " ) ;

     // Adaugarea butoanelor
     f . add ( b1 ) ;
202                                        ˘
              CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                  ¸

        f . add ( b2 ) ;
        f . pack () ;

        // Afisarea fereastrei
        f . show () ;
    }
}

    Dup˘ cum veti observa la executia acestui program, atˆt butoanele ad˘ugate
         a        ¸                   ¸                        a           a
de noi cˆt ¸i butonul de ˆ
         a s               ınchidere a ferestrei sunt functionale, adic˘ pot fi
                                                               ¸         a
apasate, dar nu realizeaz˘ nimic. Acest lucru se ˆ ampl˘ deoarece nu am
                           a                            ıntˆ     a
specificat nic˘ieri codul care trebuie s˘ se execute la ap˘sarea acestor bu-
               a                          a                      a
toane.
De asemenea, mai trebuie remarcat c˘ nu am specificat nic˘ieri dimensiu-
                                          a                          a
nile ferestrei sau ale butoanelor ¸i nici pozitiile ˆ acestea s˘ fie plasate. Cu
                                   s                ın             a
toate acestea ele sunt plasate unul lˆnga celalalt, f˘r˘ s˘ se suprapun˘ iar
                                        a                 aa a             a
suprafata fereastrei este suficient de mare cˆt s˘ cuprind˘ ambele obiecte.
        ¸                                       a a               a
Aceste ”fenomene” sunt provocate de un obiect special de tip FlowLayout
pe care l-am specificat ¸i care se ocup˘ cu gestionarea ferestrei ¸i cu plasarea
                         s              a                              s
componentelor ˆ  ıntr-o anumit˘ ordine pe suprafata ei. A¸adar, modul de
                                a                      ¸           s
aranjare nu este o caracteristic˘ a suprafetei de afi¸are ci, fiecare container
                                  a          ¸           s
are asociat un obiect care se ocup˘ cu dimensionarea ¸i dispunerea compo-
                                      a                      s
nentelor pe suprafata de afi¸are ¸i care se numeste gestionar de pozitionare
                     ¸        s     s                                    ¸
(layout manager) (vezi ”Gestionarea pozition˘rii”).
                                            ¸ a

9.2.1         Componentele AWT
Dup˘ cum am spus deja, toate componentele AWT sunt definte de clase
    a
proprii ce se gasesc ˆ pachetul java.awt, clasa Component fiind superclasa
                     ın
abstracta a tuturor acestor clase.

    • Button - butoane cu eticheta format˘ dintr-un text pe o singur˘ linie;
                                         a                          a

    • Canvas - suprafat˘ pentru desenare;
                      ¸a

    • Checkbox - component˘ ce poate avea dou˘ 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
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
                         ¸            ın
platforme de operare, acestea nu au putut fi integrate ca obiecte de tip
Component, superclasa care descrie meniuri fiind MenuComponent (vezi
”Meniuri”).


   Componentele AWT au peste 100 de metode comune, mo¸tenite din clasa
                                                             s
Component. Acestea servesc uzual pentru aflarea sau setarea atributelor
obiectelor, cum ar fi: dimensiune, pozitie, culoare, font, etc. ¸i au formatul
                                       ¸                       s
general getProprietate, respectiv setProprietate. Cele mai folosite, grupate
pe 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
204                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

   • Font
     getFont
     setFont

   • Vizibilitate
     setVisible
     isVisible

   • Interactivitate
     setEnabled
     isEnabled

9.2.2     Suprafete de afi¸are (Clasa Container)
                 ¸       s
Crearea 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               ¸          a
va 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      a
este prezentat˘ mai jos:
                a

   • Window - este superclasa tututor ferestrelor. Din aceast˘ clas˘ sunt
                                                             a     a
     derivate:

        – Frame - ferestre standard;
        – Dialog - ferestre de dialog modale sau nemodale;

   • Panel - o suprafat˘ 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                                                                   ın
cadrul containerului. Dac˘ nu este specificat nici un index la ad˘ugarea unei
                          a                                        a
componente, atunci ea va fi adaugat˘ pe ultima pozitie a listei.
                                       a                ¸
9.2. MODELUL AWT                                                         205

   Clasa Container contine metodele comune tututor suprafetelor de afi¸are.
                          ¸                               ¸          s
Dintre 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);
206                                        ˘
              CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                  ¸

9.3         Gestionarea pozition˘rii
                            ¸ a
S˘ consider˘m mai ˆ ai un exemplu de program Java care afi¸eaz˘ 5 butoane
 a          a     ıntˆ                                   s a
pe o fereastr˘:
              a

                           Listing 9.2: Pozitionarea a 5 butoane
                                            ¸
import java . awt .*;
public class TestLayout {
  public static void main ( String args []) {
    Frame f = new Frame ( " Grid Layout " ) ;
    f . setLayout ( new GridLayout (3 , 2) ) ; // *

        Button    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                                        aa
restul 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        a
dispunere a componentelor sale:
¸   ˘
9.3. GESTIONAREA POZITIONARII                                               207




   Motivul pentru care cele dou˘ ferestre arat˘ atˆt de diferit este c˘ folosesc
                                  a           a a                     a
gestionari 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                                                             ¸        s
nu este o caracteristic˘ a containerului. Fiecare obiect de tip Container
                        a
(Applet, Frame, Panel, etc.) are asociat un obiect care se ocup˘ cu dis-
                                                                       a
punerea componentelor pe suprafata sa ¸i anume gestionarul s˘u de pozitionare.
                                   ¸     s                      a        ¸
Toate clasele care instantiaza obiecte pentru gestionarea pozition˘rii imple-
                          ¸                                       ¸ a
menteaz˘ interfat˘ LayoutManager.
         a       ¸a
    La instantierea unui container se creeaz˘ implicit un gestionar de pozitionare
             ¸                              a                              ¸
asociat acestuia. De exemplu, pentru o fereastr˘ gestionarul implict este de
                                                 a
tip BorderLayout, ˆ timp ce pentru un panel este de tip FlowLayout.
                     ın


9.3.1     Folosirea gestionarilor de pozitionare
                                         ¸
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         ¸                                    s
automat la crearea sa. In cazul ˆ care acesta nu corespunde necesit˘¸ilor
                                 ın                                   at
noastre, el poate fi schimbat cu u¸urint˘. Cei mai utilizati gestionari din
                                   s   ¸a                  ¸
pachetul java.awt sunt:

   • FlowLayout

   • BorderLayout

   • GridLayout

   • CardLayout

   • GridBagLayout
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 parametru
orice instant˘ a unei clase care implementeaz˘ interfat˘ LayoutManager.
            ¸a                                    a        ¸a
Secventa de ata¸are a unui gestionar pentru un container, particularizat˘
       ¸         s                                                           a
pentru FlowLayout, este:
  FlowLayout gestionar = new FlowLayout();
  container.setLayout(gestionar);

  // sau, mai uzual:

  container.setLayout(new FlowLayout());
    Programele nu apeleaz˘ ˆ general metode ale gestionarilor de pozitionare,
                             a ın                                    ¸
dar ˆ cazul 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-
                                                           a
imesionat. Pozitiile ¸i dimensiunile componentelor nu sunt fixe, ele fiind
                  ¸      s
ajustate automat de c˘tre gestionar la fiecare redimensionare astfel ˆ at s˘
                          a                                          ıncˆ a
ocupe 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          a
ferredSize, getMinimumSize ¸i getMaximumSize care s˘ returneze di-
                                    s                          a
mensiunea implicit˘ a componentei respective ¸i limitele ˆ afara c˘rora com-
                     a                         s            ın    a
ponenta 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              a
un gestionar aceast˘ pozitionare absolut˘ a componentelor nu este posibil˘ ¸i
                      a     ¸            a                                as
deci trebuie cumva s˘ renunt˘m la gestionarea automat˘ a containerul. Acest
                       a       ¸a                        a
lucru se realizeaz˘ prin trimitera argumentului null metodei setLayout:
                   a
  // pozitionare absoluta a componentelor in container
  container.setLayout(null);
   Folosind pozitionarea absolut˘, nu va mai fi ˆ a suficient s˘ ad˘ugam cu
                ¸               a              ıns˘          a a
metoda add componentele ˆ container, ci va trebui s˘ specific˘m pozitia ¸i
                          ın                        a         a      ¸ s
¸   ˘
9.3. GESTIONAREA POZITIONARII                                                 209

dimensiunea 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                                ¸      ın
situatiile cˆnd acest lucru este posibil, deoarece permit programului s˘ aib˘
      ¸     a                                                          a    a
aceea¸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 FlowLayout
Acest gestionar a¸eaz˘ componentele pe suprafata de afi¸are ˆ flux liniar, mai
                  s a                             ¸        s    ın
precis, 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
                                                                              a
trece la urm˘toarea linie, de sus ˆ jos. Ad˘ugarea componentelor se face de
             a                     ın         a
la stˆnga la dreapta pe linie, iar alinierea obiectelor ˆ cadrul unei linii poate
     a                                                  ın
fi de trei feluri: la stˆnga, la dreapta ¸i pe centru. Implicit, componentele
                       a                  s
sunt centrate pe fiecare linie iar distanta implicit˘ ˆ
                                          ¸          a ıntre componente este de
5 pixeli pe vertical˘ ¸i 5 pe orizontal˘.
                    as                  a
    Este gestionarul implicit al containerelor derivate din clasa Panel deci ¸i s
al applet-urilor.

                     Listing 9.3: Gestionarul FlowLayout
import java . awt .*;
public class TestFlowLayout {
  public static void main ( String args []) {
    Frame f = new Frame ( " Flow Layout " ) ;
    f . setLayout ( new FlowLayout () ) ;

     Button b1 = new Button ( " Button 1 " ) ;
     Button b2 = new Button ( " 2 " ) ;
     Button b3 = new Button ( " Button 3 " ) ;
210                                        ˘
              CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                  ¸

        Button b4 = new Button ( " Long - Named Button 4 " ) ;
        Button b5 = new Button ( " Button 5 " ) ;

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

    Componentele ferestrei vor fi afi¸ate astfel:
                                   s




   Redimensionˆnd fereastra astfel ˆ at cele cinci butoane s˘ nu mai ˆ
                 a                    ıncˆ                     a     ıncap˘
                                                                          a
pe o linie, ultimele dintre ele vor fi trecute pe linia urm˘toare:
                                                          a




9.3.3        Gestionarul BorderLayout
Gestionarul BorderLayout ˆ  ımparte suprafata de afi¸are ˆ cinci regiuni, core-
                                             ¸        s   ın
spunz˘toare celor patru puncte cardinale ¸i centrului. O component˘ poate fi
       a                                  s                            a
plasat˘ ˆ oricare din aceste regiuni, dimeniunea componentei fiind calculata
       a ın
astfel ˆ at s˘ ocupe ˆ
        ıncˆ a        ıntreg spatiul de afi¸are oferit de regiunea respectiv˘.
                                 ¸         s                                  a
Pentru a ad˘uga mai multe obiecte grafice ˆ
             a                                 ıntr-una din cele cinci zone, ele
trebuie grupate ˆ prealabil ˆ
                ın          ıntr-un panel, care va fi amplasat apoi ˆ regiunea
                                                                    ın
dorit˘ (vezi ”Gruparea componentelor - clasa Panel”).
     a
    A¸adar, la ad˘ugarea unei componente pe o suprafat˘ gestionat˘ de BorderLayout,
      s          a                                      ¸a           a
metoda add va mai primi pe lˆnga referinta componentei ¸i zona ˆ care
                                 a             ¸                s        ın
aceasta va fi amplasat˘, care va fi specificat˘ prin una din constantele clasei:
                      a                      a
NORTH, SOUTH, EAST, WEST, CENTER.
    BorderLayout este gestionarul implicit pentru toate containerele care de-
scind din clasa Window, deci al tuturor tipurilor de ferestre.
¸   ˘
9.3. GESTIONAREA POZITIONARII                                               211

                         Listing 9.4: Gestionarul BorderLayout
import java . awt .*;
public class TestBorderLayout {
  public static void main ( String args []) {
    Frame f = new Frame ( " Border Layout " ) ;
    // Apelul de mai jos poate sa lipseasca
    f . setLayout ( new BorderLayout () ) ;

        f . add ( new   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                        s
sudul 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               a
Redimensionarea componentelor din fiecare zon˘ se face astfel ˆ at ele ocup˘
                                               a               ıncˆ           a
toat˘ zona containerului din care fac parte.
    a

9.3.4        Gestionarul GridLayout
Gestionarul GridLayout organizeaz˘ containerul ca un tabel cu rˆnduri ¸i
                                   a                               a     s
coloane, componentele fiind plasate ˆ celulele tabelului de la stˆnga la
                                     ın                              a
dreapta, ˆ
         ıncepˆnd cu primul rˆnd. Celulele tabelului au dimensiuni egale
               a              a
iar o component˘ poate ocupa doar o singur˘ celul˘. Num˘rul de linii ¸i
                 a                            a     a        a           s
coloane vor fi specificate ˆ constructorul gestionarului, dar pot fi modificate
                         ın
212                                        ˘
              CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                  ¸

¸i ulterior prin metodele setRows, respectiv setCols. Dac˘ num˘rul de linii
s                                                            a     a
sau coloane este 0 (dar nu ambele ˆ acela¸i timp), atunci componentele vor
                                     ın      s
fi 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 GridLayout
import java . awt .*;
public class TestGridLayout {
  public static void main ( String args []) {
    Frame f = new Frame ( " Grid Layout " ) ;
    f . setLayout ( new GridLayout (3 , 2) ) ;

        f . add ( new   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     a
coloane, 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               a

9.3.5        Gestionarul CardLayout
Gestionarul 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.
¸   ˘
9.3. GESTIONAREA POZITIONARII                                          213

Suprafata 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                                                            a
ponent˘ 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         a
ponent˘ 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
                                                                        a
spatiului disponibil ˆ situatii ˆ care componentele pot fi grupate ˆ a¸a
    ¸                 ın      ¸ ın                                    ın s
fel ˆ at utilizatorul s˘ interactioneze la un moment dat doar cu un anumit
    ıncˆ               a        ¸
grup (o carte din pachet), celelalte fiind ascunse.
O clas˘ Swing care implementeaz˘ un mecansim similar este JTabbedPane.
       a                           a


                   Listing 9.6: Gestionarul CardLayout
import java . awt .*;
import java . awt . event .*;

public class TestCardLayout extends Frame implements
   ActionListener {
  Panel tab ;
  public TestCardLayout () {
    super ( " Test CardLayout " ) ;
    Button card1 = new Button ( " Card 1 " ) ;
    Button card2 = new Button ( " Card 2 " ) ;

     Panel butoane = new Panel () ;
     butoane . add ( card1 ) ;
     butoane . add ( card2 ) ;

    tab = new Panel () ;
    tab . setLayout ( new CardLayout () ) ;

    TextField tf = new TextField ( " Text Field " ) ;
    Button btn = new Button ( " Button " ) ;
    tab . add ( " Card 1 " , tf ) ;
    tab . add ( " Card 2 " , btn ) ;

    add ( butoane , BorderLayout . NORTH ) ;
    add ( tab , BorderLayout . CENTER ) ;

     pack () ;
     show () ;
214                                       ˘
             CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                 ¸

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

    public void actionPerformed ( ActionEvent e ) {
      CardLayout gestionar = ( CardLayout ) tab . getLayout () ;
      gestionar . show ( tab , e . getActionCommand () ) ;
    }

    public static void main ( String args []) {
      TestCardLayout f = new TestCardLayout () ;
      f . show () ;
    }
}

        Prima ”carte” este vizibil˘
                                  a   A doua ”carte” este vizibil˘
                                                                 a




9.3.6       Gestionarul GridBagLayout
Este cel mai complex ¸i flexibil gestionar de pozitionare din Java. La fel ca ˆ
                        s                            ¸                            ın
cazul gestionarului GridLayout, suprafata de afi¸are este considerat˘ ca fiind
                                            ¸        s                     a
un tabel ˆ a, spre deosebire de acesta, num˘rul de linii ¸i de coloane sunt
           ıns˘                                    a             s
determinate 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 at
o component˘ poate ocupa mai multe celule adiacente, chiar de dimensiuni
               a
diferite, zona ocupat˘ fiind referit˘ prin ”regiunea de afi¸are” a componentei
                       a             a                        s
respective.
     Pentru a specifica modul de afi¸are a unei componente, acesteia ˆ este
                                        s                                     ıi
asociat un obiect de tip GridBagConstraints, ˆ care se specific˘ diferite
                                                       ın                  a
propriet˘¸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                           as
obiect GridBagConstraints se realizeaz˘ prin metoda setConstraints:
                                             a

    GridBagLayout gridBag = new GridBagLayout();
¸   ˘
9.3. GESTIONAREA POZITIONARII                                             215

  container.setLayout(gridBag);
  GridBagConstraints c = new GridBagConstraints();
  //Specificam restrictiile referitoare la afisarea componentei
  . . .
  gridBag.setConstraints(componenta, c);
  container.add(componenta);

    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                                    a
tiv˘. Aceste constrˆngeri vor fi specificate prin intermediul unui obiect de tip
   a               a
GridBagConstraints, care poate fi refolosit pentru mai multe componente
care 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
                                      a
urm˘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;
216                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸

    Ca exemplu, s˘ realiz˘m o fereastr˘ ca ˆ figura de mai jos. Pentru a
                     a       a        a     ın
simplifica codul, a fost creat˘ o metod˘ responsabil˘ cu setarea valorilor
                                  a     a          a
gridx, gridy, gridwidth, gridheight ¸i ad˘ugarea unei componente cu
                                          s    a
restrictiile stabilite pe fereastr˘.
       ¸                          a




                   Listing 9.7: Gestionarul GridBagLayout
import java . awt .*;

public class TestGridBag Layout {
  static Frame f ;
  static GridBagLayout gridBag ;
  static GridBagConstr aint s gbc ;

  static void adauga ( Component comp ,
          int x , int y , int w , int h ) {
    gbc . gridx = x ;
    gbc . gridy = y ;
    gbc . gridwidth = w ;
    gbc . gridheight = h ;

      gridBag . setConstraints ( comp , gbc ) ;
      f . add ( comp ) ;
  }

  public static void main ( String args []) {

      f = new Frame ( " Test GridBagLayout " ) ;
      gridBag = new GridBagLayout () ;

      gbc = new GridBa gC ons tr ai nt s () ;
      gbc . weightx = 1.0;
      gbc . weighty = 1.0;
¸   ˘
9.3. GESTIONAREA POZITIONARII                                         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 () ;
    }
}
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                                                                     a
gruparea componentelor ˆ   ınrudite ca functii astfel ˆ at s˘ putem fi siguri c˘,
                                           ¸          ıncˆ a                    a
indiferent 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 reprezentare
vizibil˘, rolul s˘u fiind de a oferi o suprafat˘ de afi¸are pentru componente
       a         a                             ¸a       s
grafice, inclusiv pentru alte panel-uri. Clasa care instantiaza aceste obiecte
                                                            ¸
este Panel, extensie a superclasei Container. Pentru a aranja corespunz˘tor  a
componentele grupate ˆ  ıntr-un panel, acestuia i se poate specifica un gestionar
de 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 componentelor
import java . awt .*;
public class TestPanel {
  public static void main ( String args []) {
    Frame f = new Frame ( " Test Panel " ) ;

      Panel intro = new Panel () ;
      intro . setLayout ( new GridLayout (1 , 3) ) ;
      intro . add ( new Label ( " Text : " ) ) ;
      intro . add ( new TextField ( " " , 20) ) ;
      intro . add ( new Button ( " Adaugare " ) ) ;

      Panel lista = new Panel () ;
      lista . setLayout ( new FlowLayout () ) ;
      lista . add ( new List (10) ) ;
      lista . add ( new Button ( " Stergere " ) ) ;
9.4. TRATAREA EVENIMENTELOR                                               219


        Panel control = new Panel () ;
        control . add ( new Button ( " Salvare " ) ) ;
        control . add ( new Button ( " Iesire " ) ) ;

        f . add ( intro , BorderLayout . NORTH ) ;
        f . add ( lista , BorderLayout . CENTER ) ;
        f . add ( control , BorderLayout . SOUTH ) ;

        f . pack () ;
        f . show () ;
    }
}




9.4         Tratarea evenimentelor
Un eveniment este produs de o actiune a utilizatorului asupra unei compo-
                                    ¸
nente grafice ¸i reprezint˘ mecanismul prin care utilizatorul comunic˘ efectiv
               s          a                                            a
cu programul. Exemple de evenimente sunt: ap˘sarea unui buton, modi-
                                                     a
ficarea textului ˆ ıntr-un control de editare, ˆınchiderea sau redimensionarea
unei ferestre, etc. Componentele care genereaz˘ anumite evenimente se mai
                                                  a
numesc ¸i surse de evenimente.
         s
    Interceptarea evenimentelor generate de componentele unui program se
realizeaz˘ prin intermediul unor clase de tip listener (ascult˘tor, consumator
         a                                                    a
de evenimente). In Java, orice obiect poate ”consuma” evenimentele generate
de o anumit˘ component˘ grafic˘.
             a             a      a




    A¸adar, pentru a scrie cod care s˘ se execute ˆ momentul ˆ care utiliza-
     s                               a            ın         ın
torul interactioneaz˘ cu o component˘ grafic˘ trebuie s˘ facem urm˘toarele
                    a                  a      a        a           a
lucruri:

    • s˘ scriem o clas˘ de tip listener care s˘ ”asculte” evenimentele produse
       a              a                       a
      de acea component˘ ¸i ˆ cadrul acestei clase s˘ implement˘m metode
                          a s ın                        a          a
      specifice pentru tratarea lor;
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
                                           ın
aceste obiecte se ˆımpart ˆ mai multe tipuri ˆ functie de componenta care
                           ın                  ın     ¸
le genereaz˘, mai precis ˆ functie de actiunea utilizatorului asupra acesteia.
             a           ın        ¸       ¸
Pentru fiecare tip de eveniment exist˘ o clas˘ care instantiaz˘ obiecte de
                                         a      a            ¸ a
acel tip. De exemplu, evenimentul generat de actionarea unui buton este
                                                    ¸
descris de clasa ActionEvent, cel generat de modificarea unui text de clasa
TextEvent, 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                                                          a
specifica ˆ declaratia sa c˘ dore¸te s˘ asculte evenimente de un anumit
           ın         ¸        a       s  a
tip. Acest lucru se realizeaz˘ prin implementarea unei interfete specifice
                                 a                                ¸
fiec˘rui tip de eveniment. Astfel, pentru ascultarea evenimentelor de tip
    a
ActionEvent clasa respectiv˘ trebuie s˘ implementeze interfata ActionListener,
                               a         a                    ¸
pentru TextEvent interfat˘ care trebuie implementata este TextListener,
                            ¸a
etc. Toate aceste interfete sunt derivate din EventListener.
                         ¸
    Fiecare interfat˘ define¸te una sau mai multe metode care vor fi apelate
                    ¸a       s
automat la aparitia unui eveniment:
                  ¸

class AscultaButoane implements ActionListener {
  public void actionPerformed(ActionEvent e) {
    // Metoda interfetei ActionListener
    ...
  }
}
class AscultaTexte implements TextListener {
  public void textValueChanged(TextEvent e) {
    // Metoda interfetei TextListener
    ...
  }
}

   Intrucˆt o clas˘ poate implementa oricˆte interfete, ea va putea s˘ asculte
         a        a                      a          ¸                a
evenimente de mai multe tipuri:
9.4. TRATAREA EVENIMENTELOR                                               221

class 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        ¸           s
s˘ implementeze o clas˘ consumatoare de evenimente.
 a                        a
    A¸a cum am spus mai devreme, pentru ca evenimentele unei componente
      s
s˘ fie interceptate de c˘tre o instant˘ a unei clase ascult˘tor, aceast˘ clas˘
 a                        a            ¸a                  a           a     a
trebuie ˆınregistrata ˆ lista ascult˘torilor componentei respective. Am spus
                       ın           a
lista, deoarece evenimentele unei componente pot fi ascultate de oricˆte clase,
                                                                     a
cu conditia ca acestea s˘ fie ˆ
          ¸                a   ınregistrate la componenta respectiv˘. Inregis-
                                                                   a
trarea unei clase ˆ lista ascult˘torilor unei componente se face cu metode
                    ın            a
din clasa Component de tipul addTipEvenimentListener, iar eliminarea ei
din 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 evenimentelor
Inainte de a detalia aspectele prezentate mai sus, s˘ consider˘m un exemplu
                                                     a        a
de tratare a evenimentelor. Vom crea o fereastr˘ care s˘ contin˘ dou˘ bu-
                                                  a       a     ¸ a    a
toane cu numele ”OK”, repectiv ”Cancel”. La ap˘sarea fiec˘rui buton vom
                                                    a         a
scrie pe bara de titlu a ferestrei mesajul ”Ati apasat butonul ...”.

           Listing 9.9: Ascultarea evenimentelor a dou˘ butoane
                                                      a
import java . awt .*;
import java . awt . event .*;
222                                       ˘
             CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                 ¸


class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    setLayout ( new FlowLayout () ) ;
    setSize (200 , 100) ;
    Button b1 = new Button ( " OK " ) ;
    Button b2 = new Button ( " Cancel " ) ;
    add ( b1 ) ;
    add ( b2 ) ;

         Ascultator listener = new Ascultator ( this ) ;
         b1 . addActionListener ( listener ) ;
         b2 . addActionListener ( listener ) ;
        // Ambele butoane sunt ascultate de obiectul listener ,
        // instanta a clasei Ascultator , definita mai jos
    }
}

class Ascultator implements ActionListener {
  private Fereastra f ;
  public Ascultator ( Fereastra f ) {
    this . f = f ;
  }

    // Metoda interfetei ActionListener
    public void actionPerformed ( ActionEvent e ) {
      f . setTitle ( " Ati apasat " + e . getActionCommand () ) ;
    }
}

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


    Nu este obligatoriu s˘ definim clase speciale pentru ascultarea eveni-
                         a
mentelor. In exemplul de mai sus am definit clasa Ascultator pentru a
intercepta evenimentele produse de cele dou˘ butoane ¸i din acest motiv a
                                            a           s
trebuit s˘ trimitem ca parametru constructorului clasei o referinta la fereas-
         a                                                       ¸
tra noastr˘. Mai simplu ar fi fost s˘ folosim chiar clasa Fereastra pentru
           a                       a
a trata evenimentele produse de componentele sale. Vom modifica putin ¸i ¸ s
9.4. TRATAREA EVENIMENTELOR                                             223

aplicatia pentru a pune ˆ evidenta o alt˘ modalitate de a determina com-
      ¸                 ın      ¸       a
ponenta generatoare a unui eveniment - metoda getSource.

                Listing 9.10: Tratarea evenimentelor ˆ ferestr˘
                                                     ın       a
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ActionListener {
  Button ok = new Button ( " OK " ) ;
  Button exit = new Button ( " Exit " ) ;
  int n =0;

    public Fereastra ( String titlu ) {
      super ( titlu ) ;
      setLayout ( new FlowLayout () ) ;
      setSize (200 , 100) ;
      add ( ok ) ;
      add ( exit ) ;

        ok . addActionListener ( this ) ;
        exit . addActionListener ( this ) ;
        // Ambele butoane sunt ascultate in clasa Fereastra
        // deci ascultatorul este instanta curenta : this
    }

    // Metoda interfetei ActionListener
    public void actionPerformed ( ActionEvent e ) {

        if ( e . getSource () == exit )
             System . exit (0) ; // Terminam aplicatia

        if ( e . getSource () == ok ) {
             n ++;
             this . setTitle ( " Ati apasat OK de " + n + " ori " ) ;
        }
    }
}

public class TestEvent2 {
  public static void main ( String args []) {
    Fereastra f = new Fereastra ( " Test Event " ) ;
    f . show () ;
  }
}
224                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

   A¸adar, orice clas˘ poate asculta evenimente de orice tip cu conditia s˘
     s               a                                               ¸ a
implementeze interfetele specifice acelor tipuri de evenimente.
                    ¸


9.4.2     Tipuri de evenimente
Evenimentele se ˆ
                ımpart ˆ dou˘ categorii: de nivel jos ¸i semantice.
                       ın   a                         s


    Evenimentele de nivel jos reprezint˘ o interactiune de nivel jos cum ar
                                          a          ¸
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 s
operatiunile 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 pentru
ap˘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       ¸                         a
GUI: ap˘sarea unui buton, selectarea unui articol dintr-o list˘, etc. Clasele
        a                                                     a
care descriu aceste tipuri de evenimente sunt:

                  ActionEvent          Actionare
                                         ¸
                  AdjustmentEvent      Ajustarea unei valori
                  ItemEvent            Schimbarea st˘rii
                                                    a
                  TextEvent            Schimbarea textului
9.4. TRATAREA EVENIMENTELOR                                               225

   Urm˘torul tabel prezint˘ componentele AWT ¸i tipurile de evenimente
        a                   a                      s
generate, prezentate sub forma interfetelor corespunz˘toare. Evident, eveni-
                                      ¸              a
mentele generate de o superclas˘, cum ar fi Component, se vor reg˘si ¸i pentru
                               a                                a s
toate 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                                             a
deoarece evenimentele legate de deplasarea mouse-ului sunt generate foarte
frecvent ¸i receptionarea lor poate avea un impact negativ asupra vitezei de
         s       ¸
executie, ˆ situatia cˆnd tratarea acestora nu ne intereseaz˘ ¸i dorim s˘
      ¸ ın         ¸   a                                        a s          a
trat˘m doar evenimente de tip click, de exemplu.
    a


   Orice clas˘ care trateaz˘ evenimente trebuie s˘ implementeze obligatoriu
              a            a                       a
metodele interfetelor corespunz˘toare. Tabelul de mai jos prezint˘, pentru
                 ¸              a                                   a
fiecare interfat˘, metodele puse la dispozitie ¸i care trebuie implementate de
              ¸a                              s
c˘tre clasa ascult˘tor.
 a                 a
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                   s
cate de componente diferite, este necesar s˘ putem afla, ˆ cadrul uneia din
                                               a           ın
metodele de mai sus, care este sursa evenimentului pe care ˆ trat˘m pen-
                                                                ıl      a
tru a putea reactiona ˆ consecint˘. Toate tipurile de evenimente mo¸tenesc
                 ¸     ın          ¸a                                     s
metoda getSource care returneaz˘ obiectul responsabil cu generarea eveni-
                                      a
mentului. In cazul ˆ care dorim s˘ diferentiem doar tipul componentei surs˘,
                    ın              a        ¸                                a
9.4. TRATAREA EVENIMENTELOR                                               227

putem folosi operatorul instanceof.
  public void actionPerformed(ActionEvent e) {
    Object sursa = e.getSource();
    if (sursa instanceof Button) {
      // A fost apasat un buton
      Button btn = (Button) sursa;
      if (btn == ok) {
        // A fost apasat butonul ’ok’
      }
      ...
    }
    if (sursa instanceof TextField) {
      // S-a apasat Enter dupa editarea textului
      TextField tf = (TextField) sursa;
      if (tf == nume) {
        // A fost editata componenta ’nume’
      }
      ...
    }
  }
    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                                   a
particularit˘¸i vor fi prezentate mai detaliat ˆ sectiunile dedicate fiecˆrei
            at                                  ın   ¸                   a
componente ˆ parte.
             ın

9.4.3    Folosirea adaptorilor ¸i a claselor anonime
                               s
Am vazut c˘ o clas˘ care trateaz˘ evenimente de un anumit tip trebuie s˘ im-
           a      a              a                                      a
plementeze interfata corespunz˘toare acelui tip. Aceasta ˆ
                  ¸            a                          ınseamn˘ c˘ trebuie
                                                                  a a
s˘ implementeze obligatoriu toate metodele definite de acea interfat˘, chiar
 a                                                                   ¸a
dac˘ nu specific˘ nici un cod pentru unele dintre ele. Sunt ˆ a situatii cˆnd
    a           a                                            ıns˘     ¸ a
acest lucru poate deveni sup˘rator, mai ales atunci cˆnd nu ne intereseaz˘
                             a                          a                   a
decˆt o singura metod˘ a interfetei.
    a                  a          ¸
    Un exemplu sugestiv este urm˘torul: o fereastr˘ care nu are specificat cod
                                  a                a
pentru tratarea evenimentelor sale nu poate fi ˆ  ınchis˘ cu butonul standard
                                                       a
228                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

marcat cu ’x’ din coltul dreapta sus ¸i nici cu combinatia de taste Alt+F4.
                     ¸                s                 ¸
Pentru a realiza acest lucru trebuie interceptat evenimentul de ˆ ınchidere a
ferestrei ˆ metoda windoClosing ¸i apelat˘ metoda dispose de ˆ
          ın                        s        a                      ınchidere
a ferestrei, sau System.exit pentru terminarea programului, ˆ cazul cˆnd
                                                               ın        a
este vorba de fereastra principal˘ a aplicatiei. Aceasta ˆ
                                 a         ¸             ınseamn˘ c˘ trebuie
                                                                 a a
s˘ implement˘m interfata WindowListener care are nu mai putin de ¸apte
 a             a        ¸                                       ¸      s
metode.

          Listing 9.11: Implementarea interfetei WindowListener
                                             ¸
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements WindowListener {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( this ) ;
  }

    // Metodele interfetei WindowListener
    public void windowOpened ( WindowEvent e ) {}
    public void windowClosing ( WindowEvent e ) {
      // Terminare program
      System . exit (0) ;
    }
    public void windowClosed ( WindowEvent e ) {}
    public void windowIconified ( WindowEvent e ) {}
    public void windowDeiconified ( WindowEvent e ) {}
    public void windowActivated ( WindowEvent e ) {}
    public void windowDeact ivated ( WindowEvent e ) {}
}

public class TestWind ow Lis te ne r {
  public static void main ( String args []) {
    Fereastra f = new Fereastra ( " Test WindowListener " ) ;
    f . show () ;
  }
}


    Observati c˘ trebuie s˘ implement˘m toate metodele interfetei, chiar dac˘
            ¸ a           a           a                        ¸            a
nu scriem nici un cod pentru unele dintre ele. Singura metod˘ care ne intere-
                                                             a
seaz˘ este windowClosing, ˆ care specific˘m ce trebuie f˘cut atunci cˆnd
     a                        ın            a              a              a
utilizatorul doreste s˘ ˆ
                      a ınchid˘ fereastra. Pentru a evita scrierea inutil˘ a
                               a                                          a
9.4. TRATAREA EVENIMENTELOR                                               229

acestor metode, exist˘ o serie de clase care implementeaz˘ interfetele de tip
                       a                                   a        ¸
”listener” f˘r˘ a specifica nici un cod pentru metodele lor. Aceste clase se
             aa
numesc adaptori.
    Un adaptor este o clas˘ abstract˘ care implementeaz˘ o anumit˘ interfat˘
                           a         a                   a          a      ¸a
f˘r˘ a specifica cod nici unei metode a interfetei.
 aa                                             ¸
    Scopul unei astfel de clase este ca la crearea unui ”ascult˘tor” de eveni-
                                                               a
mente, ˆ loc s˘ implement˘ o anumit˘ interfat˘ ¸i implicit toate metodele
         ın     a            a           a        ¸a s
sale, 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            a
scriem o anumit˘ secvent˘ de cod).
                  a       ¸a
    De exemplu, adaptorul interfetei WindowListener este WindowAdapter
                                    ¸
iar folosirea acestuia este dat˘ ˆ exemplul de mai jos:
                               a ın

               Listing 9.12: Extinderea clasei WindowAdapter
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowListener ( new Ascultator () ) ;
  }
}

class Ascultator extends WindowAdapter {
  // Suprdefinim metodele care ne intereseaza
  public void windowClosing ( WindowEvent e ) {
    System . exit (0) ;
  }
}

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


   Avantajul clar al acestei modalit˘¸i de tratare a evenimentelor este re-
                                    at
ducerea codului programului, acesta devenind mult mai lizibil. Ins˘ exist˘ ¸i
                                                                  a      as
dou˘ dezavantaje majore. Dup˘ cum ati observat fat˘de exemplul anterior,
   a                          a        ¸            ¸a
230                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

clasa Fereastra nu poate extinde WindowAdapter deoarece ea extinde deja
clasa Frame ¸i din acest motiv am construit o nou˘ clas˘ numit˘ Ascultator.
              s                                   a    a       a
Vom vedea ˆ a c˘ acest dezavantaj poate fi eliminat prin folosirea unei clase
             ıns˘ a
anonime.
Un alt dezavantaj este c˘ orice gre¸eal˘ de sintax˘ ˆ declararea unei metode
                         a         s a            a ın
a 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                         a
class Ascultator extends WindowAdapter {
  // In loc de windowClosing scriem WindowClosing
  // Nu supradefinim vreo metoda a clasei WindowAdapter
  // Nu da nici o eroare
  // Nu face nimic !
  public void WindowClosing(WindowEvent e) {
    System.exit(0);
  }
}
    In tabelul de mai jos sunt dati toti adaptorii interfetelor de tip ”listener”
                                 ¸ ¸                      ¸
- se oberv˘ c˘ o interfat˘ XXXListener are un adaptor de tipul XXXAdapter.
           a a          ¸a
Interfetele care nu au un adaptor sunt cele care definesc o singur˘ metod˘ ¸i
       ¸                                                             a        as
prin 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 ın
clasele anonime sunt clase interne folosite pentru instantierea unui singur
                                                          ¸
obiect de un anumit tip. Un exemplu tipic de folosire a lor este instantierea
                                                                       ¸
9.4. TRATAREA EVENIMENTELOR                                                          231

adaptorilor direct ˆ corpul unei clase care contine componente ale c˘ror
                   ın                          ¸                    a
evenimente trebuie tratate.


          Listing 9.13: Folosirea adaptorilor ¸i a claselor anonime
                                              s
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    setSize (400 , 400) ;

    this . addWindowListener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         // Terminam aplicatia
         System . exit (0) ;
       }
    }) ;

    final Label label = new Label ( " " , Label . CENTER ) ;
    label . setBackground ( Color . yellow ) ;
    add ( label , BorderLayout . NORTH ) ;


    this . addMouseListener ( new MouseAdapter () {
       public void mouseClicked ( MouseEvent e ) {
         // Desenam un cerc la fiecare click de mouse
         label . setText ( " Click ... " ) ;
         Graphics g = Fereastra . this . getGraphics () ;
         g . setColor ( Color . blue ) ;
         int raza = ( int ) ( Math . random () * 50) ;
         g . fillOval ( e . getX () , e . getY () , raza , raza ) ;
       }
    }) ;

    this . ad dM ous eM oti on Li st e n e r ( new Mo us eM ot ion Ad ap te r () {
       public void mouseMoved ( MouseEvent e ) {
         // Desenam un punct la coordonatele mouse - ului
         Graphics g = Fereastra . this . getGraphics () ;
         g . drawOval ( e . getX () , e . getY () , 1 , 1) ;
       }
    }) ;
232                                       ˘
             CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                 ¸

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

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




9.5        Folosirea ferestrelor
Dup˘ cum am v˘zut suprafetele de afi¸are ale componentelor sunt extensii
     a           a            ¸         s
ale clasei Container. O categorie aparte a acestor containere o reprezint˘
                                                                         a
ferestrele. Acestea sunt descrise de clase derivate din Window, cele mai
utilizate fiind Frame ¸i Dialog.
                        s
    O aplicatie Java cu intefat˘ grafic˘ va fi format˘ din una sau mai multe
             ¸                 ¸a     a              a
ferestre, una dintre ele fiind numit˘ fereastra principal˘.
                                   a                    a

9.5.1       Clasa Window
Clasa Window este rar utilizat˘ ˆ mod direct deoarece permite doar crearea
                               a ın
unor ferestre care nu au chenar ¸i nici bar˘ de meniuri. Este util˘ atunci
                                   s         a                      a
cˆnd dorim s˘ afi¸am ferestre care nu interactioneaz˘ cu utilizatorul ci doar
 a           a s                               ¸    a
ofer˘ anumite informatii.
    a                  ¸
    Metodele mai importante ale clasei Window, care sunt de altfel mo¸tenite
                                                                      s
de toate subclasele sale, sunt date de mai jos:

    • show - face vizibil˘ fereastra. Implicit, o fereastr˘ nou creat˘ nu este
                         a                                a          a
      vizibil˘;
             a

    • hide - face fereastra invizibil˘ f˘r˘ a o distruge ˆ a; pentru a redeveni
                                     a aa                ıns˘
      vizibila se poate apela metoda show;
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                     a

9.5.2     Clasa Frame
Este derivat˘ a clasei Window ¸i este folosit˘ pentru crearea de ferestre inde-
              a                  s           a
pendente ¸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                     a
numit˘ ¸i fereastra principal˘.
      as                       a
    Constructorii uzuali ai clasei Frame permit crearea unei ferestre cu sau
f˘r˘ titlu, initial invizibil˘. Pentru ca o fereastr˘ s˘ devin˘ vizibil˘ se va
 aa             ¸            a                      a a       a        a
apela metoda show definit˘ ˆ superclasa Window.
                             a ın

import java.awt.*;
public class TestFrame {
  public static void main(String args[]) {
    Frame f = new Frame("Titlul ferestrei");
    f.show();
  }
}

   Crearea ferestrelor prin instantierea direct˘ a obiectelor de tip Frame este
                                  ¸            a
mai putin folosit˘. De obicei, ferestrele unui program vor fi definite ˆ clase
       ¸         a                                                      ın
separate care extind clasa Frame, ca ˆ exemplul de mai jos:
                                      ın

import java.awt.*;
class Fereastra extends Frame{
  // Constructorul
  public Fereastra(String titlu) {
    super(titlu);
    ...
  }
234                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

}
public class TestFrame {
  public static void main(String args[]) {
    Fereastra f = new Fereastra("Titlul ferestrei");
    f.show();
  }
}
     Gestionarul de pozitionare implicit al clasei Frame este BorderLayout.
                          ¸
Din acest motiv, ˆ momentul ˆ care fereastra este creat˘ dar nici o compo-
                   ın              ın                         a
nent˘ grafic˘ nu este ad˘ugat˘, suprafata de afi¸are a feretrei va fi determi-
      a      a             a      a        ¸        s
nat˘ 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      a
metoda 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                               a
exemplul de mai jos:

              Listing 9.14: Structura general˘ a unei ferestre
                                             a
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ActionListener {

  // Constructorul
  public Fereastra ( String titlu ) {
    super ( titlu ) ;

      // Tratam evenimentul de inchidere a ferestrei
      this . addWindowList ener ( new WindowAdapter () {
         public void windowClosing ( WindowEvent e ) {
           dispose () ; // inchidem fereastra
           // sau terminam aplicatia
           System . exit (0) ;
         }
      }) ;
9.5. FOLOSIREA FERESTRELOR                                              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                               a
specifice 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
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 Dialog
Toate interfetele grafice ofer˘ un tip special de ferestre destinate prelu˘rii
              ¸               a                                                a
unor informatii sau a unor date de la utilizator. Acestea se numesc ferestre
              ¸
de dialog sau casete de dialog ¸i sunt implementate prin intermediul clasei
                                 s
Dialog, subclas˘ direct˘ a clasei Window.
                 a       a
    Diferenta major˘ dintre ferestrele de dialog ¸i ferestrele de tip Frame
            ¸        a                                s
const˘ ˆ faptul c˘ o fereastr˘ de dialog este dependent˘ de o alt˘ fereastra
      a ın         a           a                             a         a
(normal˘ sau tot fereastr˘ de dialog), numit˘ ¸i fereastra p˘rinte. Cu alte
         a                 a                     as               a
cuvinte, ferestrele de dialog nu au o existent˘ de sine st˘t˘toare. Cˆnd fer-
                                               ¸a             aa          a
eastra p˘rinte este distrus˘ sunt distruse ¸i ferestrele sale de dialog, cˆnd este
         a                 a               s                              a
minimizat˘ ferestrele sale de dialog sunt f˘cute invizibile iar cˆnd este restau-
           a                               a                      a
rat˘ acestea sunt aduse la starea ˆ care se g˘seau ˆ momentul minimiz˘rii
   a                               ın            a      ın                     a
ferestrei 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˘      a
structori care s˘ specifice ¸i ace¸ti parametri. Constructorii clasei Dialog
                 a           s     s
sunt:

  Dialog(Frame parinte)
  Dialog(Frame parinte, String titlu)
  Dialog(Frame parinte, String titlu, boolean modala)
  Dialog(Frame parinte, boolean modala)
  Dialog(Dialog parinte)
  Dialog(Dialog parinte, String titlu)
  Dialog(Dialog parinte, String titlu, boolean modala)
9.5. FOLOSIREA FERESTRELOR                                                   237

    Parametrul ”p˘rinte” reprezint˘ referinta la fereastra p˘rinte, ”titlu”
                    a                  a      ¸               a
reprezint˘ titlul ferestrei iar prin argumentul ”modal˘” specific˘m dac˘ fer-
          a                                           a         a       a
eastra de dialog creat˘ va fi modal˘ (true) sau nemodal˘ (false - valoarea
                        a             a                  a
implicit˘).
        a
    Crearea unei ferestre de dialog este relativ simpla ¸i se realizeaz˘ prin
                                                        s              a
derivarea clasei Dialog. Comunicarea dintre fereastra de dialog ¸i fereastra
                                                                  s
sa p˘rinte, pentru ca aceasta din urm˘ s˘ poat˘ folosi datele introduse (sau
    a                                    a a     a
optiunea specificata) ˆ caseta de dialog, se poate realiza folosind una din
  ¸                     ın
urm˘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                 a
unui ¸ir de caractere. Fereastra principal˘ a aplicatiei va fi p˘rintele casetei
      s                                    a                   a
de dialog, va primi ¸irul de caractere introdus ¸i ˆ va modifica titlul ca fiind
                     s                          s ısi
acesta. Deschiderea ferestrei de dialog se va face la ap˘sarea unui buton al
                                                         a
ferestrei 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 dialog
import java . awt .*;
import java . awt . event .*;

// Fereastra principala
class FerPrinc extends Frame implements ActionListener {
238                                      ˘
            CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                ¸

    public FerPrinc ( String titlu ) {
      super ( titlu ) ;
      this . addWindowList ener ( new WindowAdapter () {
         public void windowClosing ( WindowEvent e ) {
           System . exit (0) ;
         }
      }) ;

        setLayout ( new FlowLayout () ) ;
        setSize (300 , 80) ;
        Button b = new Button ( " Schimba titlul " ) ;
        add ( b ) ;
        b . addActionListener ( this ) ;
    }

    public void actionPerformed ( ActionEvent e ) {
      FerDialog d = new FerDialog ( this , " Dati titlul " , true ) ;
      String titlu = d . raspuns ;
      if ( titlu == null )
        return ;
      setTitle ( titlu ) ;
    }
}

// Fereastra de dialog
class FerDialog extends Dialog implements ActionListener {
  public String raspuns = null ;
  private TextField text ;
  private Button ok , cancel ;

    public FerDialog ( Frame parinte , String titlu , boolean
       modala ) {
      super ( parinte , titlu , modala ) ;
      this . addWindowList ener ( new WindowAdapter () {
         public void windowClosing ( WindowEvent e ) {
           raspuns = null ;
           dispose () ;
         }
      }) ;

        text = new TextField ( " " , 30) ;
        add ( text , BorderLayout . CENTER ) ;

        Panel panel = new Panel () ;
        ok = new Button ( " OK " ) ;
9.5. FOLOSIREA FERESTRELOR                                                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 principala
public class TestDialog {
  public static void main ( String args []) {
    FerPrinc f = new FerPrinc ( " Fereastra principala " ) ;
    f . show () ;
  }
}



9.5.4       Clasa FileDialog
Pachetul java.awt pune la dispozitie ¸i un tip de fereastr˘ de dialog folosit˘
                                      s                   a                  a
pentru selectarea unui nume de fi¸ier ˆ vederea ˆ arc˘rii sau salv˘rii unui
                                  s ın           ınc˘ a             a
fi¸ier: clasa FileDialog, derivat˘ din Dialog. Instantele acestei clase au un
 s                              a                     ¸
comportament comun dialogurilor de acest tip de pe majoritatea platformelor
de lucru, dar forma ˆ care vor fi afi¸ate este specific˘ platformei pe care
                      ın              s                 a
ruleaz˘ aplicatia.
       a      ¸
    Constructorii clasei sunt:
    FileDialog(Frame parinte)
240                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

  FileDialog(Frame parinte, String titlu)
  FileDialog(Frame parinte, String titlu, boolean mod)

Parametrul ”p˘rinte” reprezint˘ referinta ferestrei p˘rinte, ”titlu” reprezint˘
                a              a        ¸            a                        a
titlul ferestrei iar prin argumentul ”mod” specific˘m dac˘ ˆ arc˘m sau
                                                      a       a ınc˘ a
salv˘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 fisier
new FileDialog(parinte, "Alegere fisier", FileDialog.LOAD);

// Dialog pentru salvarea unui fisier
new FileDialog(parinte, "Salvare fisier", FileDialog.SAVE);

      La crearea unui obiect FileDialog acesta nu este implicit vizibil. Dac˘
                                                                            a
afi¸area sa se face cu show, caseta de dialog va fi modal˘. Dac˘ afi¸area
    s                                                       a       a s
se face cu setVisible(true), atunci va fi nemodal˘. Dup˘ selectarea unui
                                                     a      a
fi¸ier ea va fi facut˘ automat invizibil˘.
  s                 a                 a
      Pe lˆng˘ metodele mo¸tenite de la superclasa Dialog clasa FileDialog
          a a               s
mai contine metode pentru obtinerea numelui fi¸ierului sau directorului se-
          ¸                      ¸               s
lectat getFile, getDirectory, pentru stabilirea unui criteriu de filtrare
setFilenameFilter, etc.
      S˘ consider˘m un exemplu ˆ care vom alege, prin intermediul unui obiect
       a         a              ın
FileDialog, un fi¸ier cu extensia ”java”. Directorul initial este directorul
                    s                                     ¸
curent, iar numele implicit este TestFileDialog.java. Numele fi¸ieruluis
ales va fi afi¸at la consol˘.
              s           a

                Listing 9.16: Folosirea unei ferestre de dialog
import java . awt .*;
import java . awt . event .*;
import java . io .*;

class FerPrinc extends Frame implements ActionListener {

  public FerPrinc ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( new WindowAdapter () {
9.5. FOLOSIREA FERESTRELOR                                                  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           a
JFileChooser care ofer˘ mai multe facilit˘¸i ¸i prin urmare va constitui
                        a                    at s
prima optiune ˆ
        ¸     ıntr-o aplicatie cu intefat˘ grafic˘.
                           ¸            ¸a      a
242                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

9.6     Folosirea meniurilor
Spre deosebire de celelalte obiecte grafice care deriv˘ din clasa Component,
                                                     a
componentele unui meniu reprezint˘ instante ale unor clase derivate din su-
                                    a      ¸
perclasa abstract˘ MenuComponent. Aceast˘ exceptie este facut˘ deoarece
                 a                             a      ¸           a
unele 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        a
principale File ¸i Edit. Meniul Edit contine la rˆndul lui alt meniu (submeniu)
                s                       ¸        a
Options, articolul Undo ¸i dou˘ comutatoare Bold ¸i Italic. Prin abuz de
                            s    a                      s
limbaj, vom referi uneori bara de meniuri a unei ferestre ca fiind meniul
ferestrei.
    In modelul AWT obiectele care reprezint˘ bare de meniuri sunt reprezen-
                                               a
tate ca instante al clasei MenuBar. Un obiect de tip MenuBar contine obiecte
              ¸                                                      ¸
de tip Menu, care sunt de fapt meniurile derulante propriu-zise. La rˆndula
lor, acestea pot contine obiecte de tip MenuItem, CheckBoxMenuItem,
                      ¸
dar ¸i alte obiecte de tip Menu (submeniuri).
     s
9.6. FOLOSIREA MENIURILOR                                                 243

    Pentru a putea contine un meniu, o component˘ trebuie s˘ implementeze
                       ¸                           a        a
interfata MenuContainer. Cel mai adesea, meniurile sunt ata¸ate fere-
       ¸                                                         s
strelor, mai precis obiectelor de tip Frame, acestea implementˆnd interfat˘
                                                               a         ¸a
MenuContainer. Ata¸area unei bare de meniuri la o fereastr˘ se face prin
                      s                                       a
metoda addMenuBar a clasei Frame.

9.6.1    Ierarhia claselor ce descriu meniuri
S˘ vedem ˆ continuare care este ierarhia claselor folosite ˆ lucrul cu meniuri
  a        ın                                              ın
¸i s˘ analiz˘m pe rˆnd aceste clase:
s a         a      a




    Clasa MenuComponent este o clasa abstract˘ din care sunt extinse
                                                     a
toate celelalte clase folosite la crearea de meniuri, fiind similar˘ celeilalte
                                                                  a
superclase 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 ferestre
cadru de tip Frame, adaptˆnd conceptul de bar˘ de meniuri la platforma
                           a                   a
curent˘ de lucru. Dup˘ cum am mai spus, pentru a lega bara de meniuri la
      a               a
o anumit˘ fereastra trebuie apelat˘ metoda setMenuBar din clasa Frame.
         a                        a

// Crearea barei de meniuri
MenuBar mb = new MenuBar();
244                                    ˘
          CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                              ¸

// Adaugarea meniurilor derulante la bara de meniuri
...

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




    Orice articol al unui meniu trebuie s˘ 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
                                                                      ¸a
clasei MenuItem reprezint˘ de fapt un buton sau un comutator, cu o anumit˘
                          a                                              a
etichet˘ care va ap˘rea ˆ meniu, ˆ ¸it˘ eventual de un accelerator (obiect
        a          a    ın        ınsot a
de tip MenuShortcut) ce reprezint˘ combinatia de taste cu care articolul
                                    a         ¸
poate fi apelat rapid (vezi ”Acceleratori”).



    Clasa Menu permite crearea unui meniu derulant ˆ    ıntr-o bar˘ de meniuri.
                                                                  a
Optional, un meniu poate fi declarat ca fiind tear-off, ceea ce ˆ
   ¸                                                               ınseamn˘ c˘
                                                                           a a
poate fi deschis ¸i deplasat cu mouse-ul (dragged) ˆ
                 s                                    ıntr-o alt˘ pozitie decˆt
                                                                a      ¸     a
cea 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                                                                  a
care este de fapt numele s˘u ce va fi afi¸at pe bara de meniuri. Articolele
                            a             s
dintr-un meniu trebuie s˘ apartin˘ clasei MenuItem, ceea ce ˆ
                         a      ¸ a                            ınseamn˘ c˘ pot
                                                                        a a
fi instante ale uneia din clasele MenuItem, Menu sau CheckboxMenuItem.
        ¸



    Clasa CheckboxMenuItem implementeaz˘ ˆ       a ıntr-un meniu articole de
tip 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                              ın
comutator ˆ dreptul etichetei sale va fi afi¸at un simbol grafic care indic˘
            ın                              s                               a
acest lucru; la invalidarea sa, simbolul grafic respectiv va disp˘rea. Clasa
                                                                 a
CheckboxMenuItem are aceea¸i functionalitate cu cea a casetelor de validare
                              s     ¸
de tip Checkbox, ambele implementˆnd interfata ItemSelectable.
                                    a          ¸
9.6. FOLOSIREA MENIURILOR                                         245

  S˘ vedem ˆ continuare cum ar ar˘ta un program care construie¸te un
    a       ın                       a                        s
meniu ca ˆ figura prezentat˘ anterior:
         ın               a



                          Listing 9.17: Crearea unui meniu
import java . awt .*;
import java . awt . event .*;

public class TestMenu {
  public static void main ( String args []) {
    Frame f = new Frame ( " Test Menu " ) ;

        MenuBar mb = new MenuBar () ;

        Menu fisier = new Menu ( " File " ) ;
        fisier . add ( new MenuItem ( " Open " ) ) ;
        fisier . add ( new MenuItem ( " Close " ) ) ;
        fisier . addSeparator () ;
        fisier . add ( new MenuItem ( " Exit " ) ) ;

        Menu optiuni = new Menu ( " Options " ) ;
        optiuni . add ( new MenuItem ( " Copy " ) ) ;
        optiuni . add ( new MenuItem ( " Cut " ) ) ;
        optiuni . add ( new MenuItem ( " Paste " ) ) ;

        Menu editare = new Menu ( " Edit " ) ;
        editare . add ( new MenuItem ( " Undo " ) ) ;
        editare . add ( optiuni ) ;

        editare . addSeparator () ;
        editare . add ( new CheckboxMenuItem ( " Bold " ) ) ;
        editare . add ( new CheckboxMenuItem ( " Italic " ) ) ;

        mb . add ( fisier ) ;
        mb . add ( editare ) ;

        f . setMenuBar ( mb ) ;
        f . setSize (200 , 100) ;
        f . show () ;
    }
}
246                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸

9.6.2     Tratarea evenimentelor generate de meniuri
La alegerea unei optiuni dintr-un meniu se genereaz˘ fie un eveniment de tip
                       ¸                                  a
ActionEvent dac˘ articolul respectiv este de tip MenuItem, fie ItemEvent
                      a
pentru comutatoarele CheckboxMenuItem. A¸adar, pentru a activa optiunile
                                                  s                          ¸
unui meniu trebuie implementate interfatele ActionListener sau/¸i Item-
                                             ¸                             s
Listener ˆ cadrul obiectelor care trebuie s˘ specifice codul ce va fi executat
            ın                                  a
la alegerea unei optiuni ¸i implementate metodele actionPerformed, respec-
                      ¸     s
tiv itemStatChanged. Fiec˘rui meniu ˆ putem asocia un obiect receptor
                                a            ıi
diferit, ceea ce u¸ureaz˘ munca ˆ cazul ˆ care ierarhia de meniuri este com-
                  s       a        ın       ın
plex˘. Pentru a realiza leg˘tura ˆ
     a                        a    ıntre obiectul meniu ¸i obiectul de tip listener
                                                          s
trebuie s˘ adaug˘m receptorul ˆ lista de ascult˘tori a meniului respectiv,
           a        a               ın                  a
ˆ
ıntocmai ca pe orice component˘, folosind metodele addActionListener,
                                     a
respectiv addItemListener.
    A¸adar, tratarea evenimentelor generate de obiecte de tip MenuItem este
      s
identic˘ cu tratarea butoanelor, ceea ce face posibil ca unui buton de pe
        a
suprafata de 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       a
alegerii optiunii, f˘cˆndu-se o singur˘ dat˘ ˆ
             ¸       a a                 a    a ıntr-o clas˘ care este ˆ
                                                            a           ınregistrat˘
                                                                                   a
ca 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˘ a
cu List, Choice, CheckBox, toate implementˆnd interfata ItemSelectable
                                                    a           ¸
¸i deci tratarea lor va fi f˘cut˘ la fel. Tipul de operatie selectare / deselectare
s                           a a
este codificat ˆ evenimentul generat de cˆmpurile statice ItemEvent.SELECTED
               ın                           a
¸i ItemEvent.DESELECTED.
s

               Listing 9.18: Tratarea evenimentelor unui meniu
import java . awt .*;
import java . awt . event .*;

public class TestMenuEvent extends Frame
     implements ActionListener , ItemListener {

   public TestMenuEvent ( String titlu ) {
     super ( titlu ) ;

      MenuBar mb = new MenuBar () ;
      Menu test = new Menu ( " Test " ) ;
      CheckboxMenuItem check = new CheckboxMenuItem ( " Check me " )
         ;
9.6. FOLOSIREA MENIURILOR                                              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              s
clasei PopupMenu, subclas˘ direct˘ a clasei Menu. Sunt meniuri invizibile
                             a      a
care sunt activate uzual prin ap˘sarea butonului drept al mouse-ului, fiind
                                a
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           aa
s˘u drept. Metodele de ad˘ugare a articolelor unui meniu de context sunt
 a                          a
mo¸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 avea
s                               a
acces rapid la meniu. Argumentul ”origine” reprezint˘ componenta fat˘ de
                                                          a                 ¸a
originile c˘reia se va calcula pozitia de afi¸are a meniului popup. De obicei,
           a                       ¸         s
reprezint˘ 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         a
determin˘ dac˘ un eveniment de tip MouseEvent poate fi responsabil cu
          a     a
deschiderea unui meniu de context. Aceasta este isPopupTrigger ¸i este    s
definit˘ ˆ 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                       a
ci se ata¸eaz˘ la o component˘ (de obicei la o fereastr˘) prin metoda add
          s a                    a                          a
a acesteia. In cazul cˆnd avem mai multe meniuri popup pe care vrem s˘
                        a                                                       a
le folosim ˆıntr-o fereastr˘, trebuie s˘ le definim pe toate ¸i, la un moment
                           a           a                       s
dat, vom ad˘uga ferestrei meniul corespunz˘tor dup˘ care ˆ vom face vizibil.
              a                                a       a      ıl
Dup˘ ˆ
     a ınchiderea acestuia, vom ”rupe” leg˘tura ˆ
                                             a     ıntre fereastr˘ ¸i meniu prin
                                                                  as
instructiunea remove:
        ¸
  fereastra.add(popup1);
  ...
  fereastra.remove(popup1);
  fereastra.add(popup2);
    In exemplul de mai jos, vom crea un meniu de contex pe care ˆ vom activa
                                                                ıl
la ap˘sarea butonului drept al mouse-ului pe suprafata ferestrei principale.
      a                                               ¸
Tratarea evenimentelor generate de un meniu popup se realizeaz˘ identic ca
                                                                  a
pentru meniurile fixe.
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) ;
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                 a
intermediul tastaturii, a optiunilor dintr-un meniu. Astfel, oric˘rui obiect
                             ¸                                    a
de tip MenuItem ˆ poate fi asociat un obiect de tip accelerator, definit prin
                  ıi
intermediul clasei MenuShortcut. Singurele combinatii de taste care pot
                                                         ¸
juca rolul acceleratorilor sunt: Ctrl + Tasta sau Ctrl + Shift + Tasta.
Atribuirea unui accelerator la un articol al unui meniu poate fi realizat˘ prin
                                                                        a
constructorul obiectelor de tip MenuItem ˆ forma:
                                           ın
MenuItem(String eticheta, MenuShortcut accelerator), ca ˆ exempleleın
de mai jos:

    // Ctrl+O
    new MenuItem("Open", new MenuShortcut(KeyEvent.VK_O));

    // Ctrl+P
    new MenuItem("Print", new MenuShortcut(’p’));

    // Ctrl+Shift+P
    new MenuItem("Preview", new MenuShortcut(’p’), true);


9.7      Folosirea componentelor AWT
In continuare vor fi date exemple de folosire ale componentelor AWT, ˆ care
                                                                      ın
s˘ 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
9.7. FOLOSIREA COMPONENTELOR AWT                                        251

9.7.1    Clasa Label
Un obiect de tip Label (etichet˘) reprezint˘ o component˘ pentru plasarea
                                  a        a               a
unui text pe o suprafata de afi¸are. O etichet˘ este format˘ dintr-o singur˘
                        ¸       s            a              a              a
linie de text static ce nu poate fi modificat de c˘tre utilizator, dar poate fi
                                                a
modificat din program.




                     Listing 9.20: Folosirea clasei Label
import java . awt .*;
public class TestLabel {
  public static void main ( String args []) {
    Frame f = new Frame ( " Label " ) ;

     Label nord , sud , est , vest , centru ;

     nord = new Label ( " Nord " , Label . CENTER ) ;
     nord . setForeground ( Color . blue ) ;

     sud = new Label ( " Sud " , Label . CENTER ) ;
     sud . setForeground ( Color . red ) ;

     vest = new Label ( " Vest " , Label . LEFT ) ;
     vest . setFont ( new Font ( " Dialog " , Font . ITALIC , 14) ) ;

     est = new Label ( " Est " , Label . RIGHT ) ;
     est . setFont ( new Font ( " Dialog " , Font . ITALIC , 14) ) ;

     centru = new Label ( " Centru " , Label . CENTER ) ;
     centru . setBackground ( Color . yellow ) ;
     centru . setFont ( new Font ( " Arial " , Font . BOLD , 20) ) ;

     f . add ( nord , BorderLayout . NORTH ) ;
     f . add ( sud , BorderLayout . SOUTH ) ;
     f . add ( est , BorderLayout . EAST ) ;
     f . add ( vest , BorderLayout . WEST ) ;
252                                       ˘
             CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                 ¸

        f . add ( centru , BorderLayout . CENTER ) ;

        f . pack () ;
        f . show () ;
    }
}



9.7.2        Clasa Button
Un obiect de tip Button reprezint˘ o component˘ pentru plasarea unui bu-
                                  a             a
ton etichetat pe o suprafata de afi¸are. Textul etichetei este format dintr-o
                          ¸       s
singur˘ linie.
      a




                        Listing 9.21: Folosirea clasei Button
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ActionListener {

    public Fereastra ( String titlu ) {
      super ( titlu ) ;
      this . addWindowList ener ( new WindowAdapter () {
         public void windowClosing ( WindowEvent e ) {
           System . exit (0) ;
         }
      }) ;

        setLayout ( null ) ;
        setSize (200 , 120) ;

        Button b1 = new Button ( " OK " ) ;
        b1 . setBounds (30 , 30 , 50 , 70) ;
        b1 . setFont ( new Font ( " Arial " , Font . BOLD , 14) ) ;
9.7. FOLOSIREA COMPONENTELOR AWT                                         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 Checkbox

Un obiect de tip Checkbox (comutator) reprezint˘ o component˘ care se
                                                     a              a
poate 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                ın
tar˘ celei ˆ care se g˘sea. Este folosit pentru a prelua o anumit˘ optiune de
   a       ın         a                                          a ¸
la utilizator.
254                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸




                    Listing 9.22: Folosirea clasei Checkbox
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ItemListener {
  private Label label1 , label2 ;
  private Checkbox cbx1 , cbx2 , cbx3 ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

      setLayout ( new GridLayout (5 , 1) ) ;
      label1 = new Label ( " Ingrediente Pizza : " , Label . CENTER ) ;
      label1 . setBackground ( Color . orange ) ;
      label2 = new Label ( " " ) ;
      label2 . setBackground ( Color . lightGray ) ;

      cbx1 = new Checkbox ( " cascaval " ) ;
      cbx2 = new Checkbox ( " sunca " ) ;
      cbx3 = new Checkbox ( " ardei " ) ;

      add ( label1 ) ;
      add ( label2 ) ;
      add ( cbx1 ) ;
      add ( cbx2 ) ;
      add ( cbx3 ) ;
9.7. FOLOSIREA COMPONENTELOR AWT                                     255


        setSize (200 , 200) ;

        cbx1 . addItemListener ( this ) ;
        cbx2 . addItemListener ( this ) ;
        cbx3 . addItemListener ( this ) ;
    }

    // Metoda interfetei ItemListener
    public void itemStateChanged ( ItemEvent e ) {
      StringBuffer ingrediente = new StringBuffer () ;
      if ( cbx1 . getState () == true )
        ingrediente . append ( " cascaval " ) ;
      if ( cbx2 . getState () == true )
        ingrediente . append ( " sunca " ) ;
      if ( cbx3 . getState () == true )
        ingrediente . append ( " ardei " ) ;
      label2 . setText ( ingrediente . toString () ) ;
    }
}

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




9.7.4       Clasa CheckboxGroup


Un obiect de tip CheckboxGroup define¸te un grup de comutatoare din care
                                      s
doar unul poate fi selectat. Uzual, aceste componente se mai numesc bu-
toane radio. Aceast˘ clas˘ nu este derivat˘ din Component, oferind doar o
                   a     a                a
modalitate de grupare a componentelor de tip Checkbox.
256                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸




                 Listing 9.23: Folosirea clasei CheckboxGroup
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ItemListener {
  private Label label1 , label2 ;
  private Checkbox cbx1 , cbx2 , cbx3 ;
  private CheckboxGroup cbg ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

      setLayout ( new GridLayout (5 , 1) ) ;
      label1 = new Label ( " Alegeti postul TV " , Label . CENTER ) ;
      label1 . setBackground ( Color . orange ) ;
      label2 = new Label ( " " , Label . CENTER ) ;
      label2 . setBackground ( Color . lightGray ) ;

      cbg = new CheckboxGroup () ;
      cbx1 = new Checkbox ( " HBO " , cbg , false ) ;
      cbx2 = new Checkbox ( " Discovery " , cbg , false ) ;
      cbx3 = new Checkbox ( " MTV " , cbg , false ) ;

      add ( label1 ) ;
      add ( label2 ) ;
      add ( cbx1 ) ;
9.7. FOLOSIREA COMPONENTELOR AWT                                         257

        add ( cbx2 ) ;
        add ( cbx3 ) ;

        setSize (200 , 200) ;

        cbx1 . addItemListener ( this ) ;
        cbx2 . addItemListener ( this ) ;
        cbx3 . addItemListener ( this ) ;
    }

    // Metoda interfetei ItemListener
    public void itemStateChanged ( ItemEvent e ) {
      Checkbox cbx = cbg . getS e lec ted Che ck box () ;
      if ( cbx != null )
        label2 . setText ( cbx . getLabel () ) ;
    }
}

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



9.7.5        Clasa Choice
Un obiect de tip Choice define¸te o list˘ de optiuni din care utilizatorul
                                  s         a    ¸
poate selecta una singur˘. La un moment dat, din ˆ
                         a                         ıntreaga list˘ doar o sin-
                                                                a
gur˘ optiune este vizibil˘, cea selectat˘ ˆ momentul curent. O component˘
   a ¸                   a              a ın                                a
Choice este ˆ ¸it˘ de un buton etichetat cu o sageat˘ vertical˘ la ap˘sarea
             ınsot a                                  a         a     a
c˘ruia este afi¸at˘ ˆ
 a             s a ıntreaga sa list˘ de elemente, pentru ca utilizatorul s˘
                                     a                                      a
poat˘ selecta o anumit˘ optiune.
    a                  a ¸
258                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸

                         Listing 9.24: Folosirea clasei Choice
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ItemListener {
  private Label label ;
  private Choice culori ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

      setLayout ( new GridLayout (4 , 1) ) ;
      label = new Label ( " Alegeti culoarea " ) ;
      label . setBackground ( Color . red ) ;

      culori = new Choice () ;
      culori . add ( " Rosu " ) ;
      culori . add ( " Verde " ) ;
      culori . add ( " Albastru " ) ;
      culori . select ( " Rosu " ) ;

      add ( label ) ;
      add ( culori ) ;

      setSize (200 , 100) ;

      culori . addItemListener ( this ) ;
  }

  // Metoda interfetei ItemListener
  public void itemStateChanged ( ItemEvent e ) {
    switch ( culori . getSelectedIndex () ) {
      case 0:
        label . setBackground ( Color . red ) ;
        break ;
      case 1:
        label . setBackground ( Color . green ) ;
        break ;
      case 2:
        label . setBackground ( Color . blue ) ;
9.7. FOLOSIREA COMPONENTELOR AWT                                          259

        }
    }
}

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



9.7.6       Clasa List
Un obiect de tip List define¸te o list˘ de optiuni care poate fi setat˘ astfel
                                s       a      ¸                      a
ˆ at utilizatorul s˘ poat˘ selecta o singur˘ optiune sau mai multe. Toate
ıncˆ                a      a                 a ¸
elementele listei sunt vizibile ˆ limita dimensiunilor grafice ale componentei.
                                ın




                     Listing 9.25: Folosirea clasei List
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements ItemListener {
  private Label label ;
  private List culori ;

    public Fereastra ( String titlu ) {
      super ( titlu ) ;
      this . addWindowListener ( new WindowAdapter () {
260                                       ˘
             CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                 ¸

           public void windowClosing ( WindowEvent e ) {
             System . exit (0) ;
           }
        }) ;

        setLayout ( new GridLayout (2 , 1) ) ;
        label = new Label ( " Alegeti culoarea " , Label . CENTER ) ;
        label . setBackground ( Color . red ) ;

        culori = new List (3) ;
        culori . add ( " Rosu " ) ;
        culori . add ( " Verde " ) ;
        culori . add ( " Albastru " ) ;
        culori . select (3) ;

        add ( label ) ;
        add ( culori ) ;

        setSize (200 , 200) ;

        culori . addItemListener ( this ) ;
    }

    // Metoda interfetei ItemListener
    public void itemStateChanged ( ItemEvent e ) {
      switch ( culori . getSelectedIndex () ) {
        case 0:
          label . setBackground ( Color . red ) ;
          break ;
        case 1:
          label . setBackground ( Color . green ) ;
          break ;
        case 2:
          label . setBackground ( Color . blue ) ;
      }
    }
}

public class TestList {
  public static void main ( String args []) {
    Fereastra f = new Fereastra ( " List " ) ;
    f . show () ;
  }
}
9.7. FOLOSIREA COMPONENTELOR AWT                                      261

9.7.7      Clasa ScrollBar
Un obiect de tip Scrollbar define¸te o bar˘ de defilare pe vertical˘ sau
                                    s        a                       a
orizontal˘. Este util˘ pentru punerea la dispozitia utilizatorului a unei
         a            a                            ¸
modalit˘¸i sugestive de a alege o anumit˘ valoare dintr-un interval.
        at                              a




                    Listing 9.26: Folosirea clasei ScrollBar
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements Ad ju st me nt Lis te ne r {
  private Scrollbar scroll ;
  private Label valoare ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowListener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

      setLayout ( new GridLayout (2 , 1) ) ;

      valoare = new Label ( " " , Label . CENTER ) ;
      valoare . setBackground ( Color . lightGray ) ;

      scroll = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 ,
          101) ;
      add ( valoare ) ;
      add ( scroll ) ;

      setSize (200 , 80) ;

      scroll . add Adjus tmen tLis t e n e r ( this ) ;
  }

  // Metoda interfetei Adju st me ntL is te ne r
262                                      ˘
            CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                                ¸

    public void ad ju st m e n t V a lu eC ha n g e d ( AdjustmentEvent e ) {
      valoare . setText ( scroll . getValue () + " % " ) ;
    }
}

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



9.7.8      Clasa ScrollPane
Un obiect de tip ScrollPane permite ata¸area unor bare de defilare (ori-
                                            s
zontal˘ ¸si/sau vertical˘) oric˘rei componente grafice. Acest lucru este util
      as                a      a
pentru acele componente care nu au implementat˘ functionalitatea de defi-
                                                   a     ¸
lare automat˘, cum ar fi listele (obiecte din clasa List).
             a




                    Listing 9.27: Folosirea clasei ScrollPane
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame {
private ScrollPane sp ;
private List list ;

    public Fereastra ( String titlu ) {
      super ( titlu ) ;
      this . addWindowList ener ( new WindowAdapter () {
        public void windowClosing ( WindowEvent e ) {
           System . exit (0) ;
9.7. FOLOSIREA COMPONENTELOR AWT                                          263

           }
        }) ;

        list = new List (7) ;
        list . add ( " Luni " ) ;
        list . add ( " Marti " ) ;
        list . add ( " Miercuri " ) ;
        list . add ( " Joi " ) ;
        list . add ( " Vineri " ) ;
        list . add ( " Sambata " ) ;
        list . add ( " Duminica " ) ;
        list . select (1) ;

        sp = new ScrollPane ( ScrollPane . SC ROLLBAR S_ALWAY S ) ;
        sp . add ( list ) ;

        add ( sp , BorderLayout . CENTER ) ;

        setSize (200 , 200) ;
    }

}

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



9.7.9          Clasa TextField
Un obiect de tip TextField define¸te un control de editare a textului pe o
                                     s
singur˘ linie. Este util pentru interogarea utilizatorului asupra unor valori.
      a
264                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸

                   Listing 9.28: Folosirea clasei TextField
import java . awt .*;
import java . awt . event .*;

class Fereastra extends Frame implements TextListener {
  private TextField nume , parola ;
  private Label acces ;
  private static final String UID = " Duke " , PWD = " java " ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

      setLayout ( new GridLayout (3 , 1) ) ;
      setBackground ( Color . lightGray ) ;

      nume = new TextField ( " " , 30) ;
      parola = new TextField ( " " , 10) ;
      parola . setEchoChar ( ’* ’) ;

      Panel p1 = new Panel () ;
      p1 . setLayout ( new FlowLayout ( FlowLayout . LEFT ) ) ;
      p1 . add ( new Label ( " Nume : " ) ) ;
      p1 . add ( nume ) ;

      Panel p2 = new Panel () ;
      p2 . setLayout ( new FlowLayout ( FlowLayout . LEFT ) ) ;
      p2 . add ( new Label ( " Parola : " ) ) ;
      p2 . add ( parola ) ;

      acces = new Label ( " Introduceti numele si parola ! " , Label .
          CENTER ) ;
      add ( p1 ) ;
      add ( p2 ) ;
      add ( acces ) ;

      setSize (350 , 100) ;

      nume . addTextListener ( this ) ;
      parola . addTextListener ( this ) ;
  }
9.7. FOLOSIREA COMPONENTELOR AWT                                          265


    // Metoda interfetei TextListener
    public void textValueChanged ( TextEvent e ) {
      if ( nume . getText () . length () == 0 ||
           parola . getText () . length () == 0) {
        acces . setText ( " " ) ;
        return ;
      }
      if ( nume . getText () . equals ( UID ) &&
           parola . getText () . equals ( PWD ) )
        acces . setText ( " Acces permis ! " ) ;
      else
        acces . setText ( " Acces interzis ! " ) ;
    }
}

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



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




                   Listing 9.29: Folosirea clasei TextArea
import java . awt .*;
266                                     ˘
           CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                               ¸

import java . awt . event .*;
import java . io .*;

class Fereastra extends Frame implements TextListener ,
   ActionListener {
  private TextArea text ;
  private TextField nume ;
  private Button salvare ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowList ener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

      setBackground ( Color . lightGray ) ;
      text = new TextArea ( " " , 30 , 10 , TextArea .
         S C RO L L BA R S _ VE R T I C A L _ O N L Y ) ;
      nume = new TextField ( " " , 12) ;
      salvare = new Button ( " Salveaza text " ) ;
      salvare . setEnabled ( false ) ;

      Panel fisier = new Panel () ;
      fisier . add ( new Label ( " Fisier : " ) ) ;
      fisier . add ( nume ) ;

      add ( fisier , BorderLayout . NORTH ) ;
      add ( text , BorderLayout . CENTER ) ;
      add ( salvare , BorderLayout . SOUTH ) ;

      setSize (300 , 200) ;

      text . addTextListener ( this ) ;
      salvare . addActionLi stener ( this ) ;
  }

  // Metoda interfetei TextListener
  public void textValueChanged ( TextEvent e ) {
    if ( text . getText () . length () == 0 ||
         nume . getText () . length () == 0)
      salvare . setEnabled ( false ) ;
    else
      salvare . setEnabled ( true ) ;
9.7. FOLOSIREA COMPONENTELOR AWT                      267

    }

    // Metoda interfetei ActionListener
    public void actionPerformed ( ActionEvent e ) {
      String continut = text . getText () ;
      try {
        PrintWriter out = new PrintWriter (
           new FileWriter ( nume . getText () ) ) ;
        out . print ( continut ) ;
        out . close () ;
        text . requestFocus () ;
      } catch ( IOException ex ) {
        ex . printStackTrace () ;
      }
    }
}

public class TestTextArea {
  public static void main ( String args []) {
    Fereastra f = new Fereastra ( " TextArea " ) ;
    f . show () ;
  }
}
268                                ˘
      CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL
                          ¸
Capitolul 10

Desenarea

10.1      Conceptul de desenare
Un program Java care are interfat˘ grafic˘ cu utilizatorul trebuie s˘ deseneze
                                 ¸a     a                          a
pe ecran toate componentele sale care au o reprezentare vizual˘. Aceast˘
                                                                  a         a
desenare include componentele standard folosite ˆ aplicatie precum ¸i cele
                                                  ın       ¸           s
definite de c˘tre programator. Desenarea componentelor se face automat ¸i
            a                                                              s
este un proces care se execut˘ ˆ urm˘toarele situatii:
                             a ın    a              ¸

   • la afi¸area pentru prima dat˘ a unei componente;
          s                     a

   • la operatii de minimizare, maximizare, redimensionare a suprafetei de
             ¸                                                      ¸
     afi¸are;
        s

   • ca r˘spuns al unei solicit˘ri explicite a programului.
         a                     a

    Metodele care controleaz˘ procesul de desenare se g˘sesc ˆ clasa Component
                            a                          a     ın
¸i sunt urm˘toarele:
s          a

   • void paint(Graphics g) - Deseneaz˘ o component˘. Este o metod˘
                                            a             a               a
     supradefinit˘ de fiecare component˘ ˆ parte pentru a furniza reprezentarea
                 a                     a ın
     sa grafic˘ specific˘. Metoda este apelat˘ de fiecare dat˘ cˆnd continutul
             a        a                      a            a a       ¸
     componentei trebuie desenat sau redesenat ¸i nu va fi apelat˘ explicit.
                                                 s               a

   • void update(Graphics g) - Actualizeaz˘ starea grafic˘ a unei com-
                                              a             a
     ponente. Actiunea acestei metode se realizeaz˘ ˆ trei pa¸i:
                ¸                                 a ın       s

       1. ¸terge componenta prin supradesenarea ei cu culoarea fundalului;
          s

                                    269
270                                       CAPITOLUL 10. DESENAREA

       2. stabile¸te culoarea (foreground) a componentei;
                 s
       3. apeleaz˘ metoda paint pentru a redesena componenta.
                 a

   • void repaint() - Execut˘ explicit un apel al metodei update pentru
                               a
     a actualiza reprezentarea grafic˘ a unei componente.
                                    a

    Dup˘ cum se observ˘, singurul argument al metodelor paint ¸i update
        a               a                                         s
este un obiect de tip Graphics. Acesta reprezint˘ contextul grafic ˆ care se
                                                a                 ın
execut˘ desenarea componentelor (vezi ”Contextul grafic de desenare - clasa
      a
Graphics”).


    Atentie
          ¸
    Toate desenele care trebuie s˘ apar˘ pe o suprafata de desenare se real-
                                 a      a            ¸
izeaz˘ ˆ metoda paint a unei componente, ˆ general apelat˘ automat sau
     a ın                                    ın              a
explicit cu metoda repaint ori de cˆte ori componenta respectiv˘ trebuie
                                      a                           a
redesenat˘. Exist˘ posibilitatea de a desena ¸i ˆ afara metodei paint, ˆ a
          a      a                           s ın                       ıns˘
aceste desene se vor pierde la prima operatie de minimizare, maximizare,
                                            ¸
redimensionare a suprafetei de afi¸are.
                         ¸        s




10.1.1     Metoda paint
Dup˘ cum am spus, toate desenele care trebuie s˘ apar˘ pe o suprafat˘ de
      a                                          a      a             ¸a
afi¸are se realizeaz˘ ˆ metoda paint a unei componente. Metoda paint este
   s                a ın
definit˘ ˆ superclasa Component ˆ a nu are nici o implementare ¸i, din acest
        a ın                     ıns˘                           s
motiv, orice obiect grafic care dore¸te s˘ se deseneze trebuie s˘ o suprade-
                                    s   a                      a
fineasc˘ pentru a-¸i crea propria sa reprezentare. Componentele standard
        a           s
AWT au deja supradefinit˘ aceast˘ metod˘ deci nu trebuie s˘ ne preocupe
                          a        a       a                 a
desenarea lor, ˆ a putem modifica reprezentarea lor grafic˘ prin crearea
                 ıns˘                                        a
unei subclase ¸i supradefinirea metodei paint, avˆnd ˆ a grij˘ s˘ apel˘m ¸i
               s                                 a ıns˘      a a      a s
metoda superclasei care se ocup˘ cu desenarea efectiv˘ a componentei.
                                a                    a
     In exemplul de mai jos, redefinim metoda paint pentru un obiect de
tip Frame, pentru a crea o clas˘ ce instantiaz˘ ferestre pentru o aplicatie
                                a           ¸ a                          ¸
demonstrativ˘ (ˆ coltul stˆnga sus este afi¸at textul ”Aplicatie DEMO”).
              a ın ¸       a               s
10.1. CONCEPTUL DE DESENARE                                                  271

                  Listing 10.1: Supradefinirea metodei paint
import java . awt .*;
class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    setSize (200 , 100) ;
  }

    public void paint ( Graphics g ) {
      // Apelam metoda paint a clasei Frame
      super . paint ( g ) ;
      g . setFont ( new Font ( " Arial " , Font . BOLD , 11) ) ;
      g . setColor ( Color . red ) ;
      g . drawString ( " Aplicatie DEMO " , 5 , 35) ;
    }
}

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


    Observati c˘ la orice redimensionare a ferestrei textul ”Aplicatie DEMO”
           ¸ a
va fi redesenat. Dac˘ desenarea acestui text ar fi fost facut˘ oriunde ˆ alt˘
                     a                                        a         ın a
parte decˆt ˆ metoda paint, la prima redimensionare a ferestrei acesta s-ar
         a ın
pierde.
    A¸adar, desenarea ˆ Java trebuie s˘ se fac˘ doar ˆ cadrul metodelor
     s                  ın               a        a       ın
paint ale componentelor grafice.


10.1.2       Suprafete de desenare - clasa Canvas
                    ¸
In afara posibilit˘¸ii de a utiliza componente grafice standard, Java ofer˘
                  at                                                            a
¸i posibilitatea controlului la nivel de punct (pixel) pe dispozitivul grafic,
s
respectiv desenarea a diferite forme grafice direct pe suprafata unei compo-
                                                                 ¸
nente. De¸i este posibil, ˆ general nu se deseneaz˘ la nivel de pixel direct
           s               ın                         a
pe suprafata ferestrelor sau a altor containere, ci vor fi folosite clase dedicate
            ¸
acestui scop.
    In AWT a fost definit un tip special de component˘ numit˘ Canvas
                                                             a         a
(pˆnz˘ de pictor), al c˘rei scop este de a fi extins˘ pentru a implementa
   a a                   a                              a
272                                         CAPITOLUL 10. DESENAREA

obiecte grafice cu o anumit˘ ˆ a¸i¸are. A¸adar, Canvas este o clas˘ generic˘
                            a ınf˘t s      s                        a         a
din care se deriveaz˘ subclase pentru crearea suprafetelor de desenare (plan¸e).
                    a                                 ¸                     s
Plan¸ele nu pot contine alte componente grafice, ele fiind utilizate doar ca
      s               ¸
suprafete de desenat sau ca fundal pentru animatie. Desenarea pe o plan¸a
        ¸                                           ¸                        s
se face prin supradefinirea metodei paint a acesteia.
    Concret, o plan¸˘ este o suprafat˘ dreptunghiular˘ de culoare alb˘, pe
                    sa                ¸a                 a                a
care se poate desena. Dimensiunile sale implicite sunt 0 ¸i, din acest mo-
                                                              s
tiv, este recomandat ca o plan¸a s˘ redefineasca metoda getPreferredSize,
                                s a
eventual ¸i getMinimumSize, getMaximumSize, deoarece acestea vor fi apelate
          s
de c˘tre gestionarii de pozitionare.
     a                      ¸
    Etapele uzuale care trebuie parcurse pentru crearea unui desen, sau mai
bine zis a unei componente cu o anumit˘ ˆ a¸i¸are, sunt:
                                          a ınf˘t s

   • crearea unei plan¸e de desenare, adic˘ o subclas˘ a lui Canvas;
                      s                   a          a

   • redefinirea metodei paint din clasa respectiv˘;
                                                 a

   • redefinirea metodelor getPreferredSize, eventual getMinimumSize,
     getMaximumSize;

   • ad˘ugarea plan¸ei pe un container cu metoda add.
       a           s

   • tratarea evenimentelor de tip FocusEvent, KeyEvent, MouseEvent,
     ComponentEvent, dac˘ este cazul.
                          a

   Definirea generic˘ a unei plan¸e are urm˘torul format:
                   a            s         a

class Plansa extends Canvas implements ...Listener {

  //Eventual, unul sau mai multi constructori
  public Plansa() {
    ...
  }
  // Metode de desenare a componentei
  public void paint(Graphics g) {
  ...
  }
  // Metodele folosite de gestionarii de pozitionare
  public Dimension getPreferredSize() {
    // Dimensiunea implicita a plansei
10.1. CONCEPTUL DE DESENARE                                               273

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

    S˘ definim o plan¸˘ pe care desen˘m un p˘trat ¸i cercul s˘u circumscris,
     a                 sa              a       a     s        a
colorate diferite. La fiecare click de mouse, vom interschimba cele dou˘ culori
                                                                      a
ˆ
ıntre ele.

                      Listing 10.2: Folosirea clasei Canvas
import java . awt .*;
import java . awt . event .*;

class Plansa extends Canvas {
  Dimension dim = new Dimension (100 , 100) ;
  private Color color [] = { Color . red , Color . blue };
  private int index = 0;

    public Plansa () {
      this . addMouseListener ( new MouseAdapter () {
         public void mouseClicked ( MouseEvent e ) {
           index = 1 - index ;
           repaint () ;
         }
      }) ;
    }

    public void paint ( Graphics g ) {
      g . setColor ( color [ index ]) ;
      g . drawRect (0 , 0 , dim . width , dim . height ) ;
      g . setColor ( color [1 - index ]) ;
      g . fillOval (0 , 0 , dim . width , dim . height ) ;
    }

    public Dimension getPreferredSize () {
274                                         CAPITOLUL 10. DESENAREA

        return dim ;
    }
}

class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    setSize (200 , 200) ;
    add ( new Plansa () , BorderLayout . CENTER ) ;
  }
}

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




10.2        Contextul grafic de desenare
Inainte ca utilizatorul s˘ poat˘ desena, el trebuie s˘ obtin˘ un context grafic
                         a     a                     a ¸ a
de desenare pentru suprafata c˘reia ˆ apartine regiunea pe care se va desena.
                            ¸ a      ıi     ¸
Un context grafic este, de fapt, un obiect prin intermediul c˘ruia putem
                                                                  a
controla procesul de desenare a unui obiect. In general, desenarea se poate
face:

    • pe o portiune de ecran,
              ¸

    • la imprimant˘ sau
                  a

    •ˆ
     ıntr-o zon˘ virtual˘ de memorie.
               a        a

    Un context grafic este specificat prin intermediul unui obiect de tip Graph-
ics primit ca parametru ˆ metodele paint ¸i update. In functie de dispozi-
                          ın                  s                 ¸
tivul fizic pe care se face afi¸area (ecran, imprimant˘, plotter, etc) metodele
                              s                      a
de desenare au implement˘ri interne diferite, transparente utilizatorului.
                            a
    Clasa Graphics pune la dispozitie metode pentru:
                                     ¸

    • primitive grafice: desenarea de figuri geometrice, texte ¸i imagini
                                                             s

    • stabilirea propriet˘¸ilor contextului grafic, adic˘ stabilirea:
                         at                            a
10.2. CONTEXTUL GRAFIC DE DESENARE                                           275

         – culorii ¸i fontului curente cu care se face desenarea,
                   s
         – originii coordonatelor suprafetei de desenare,
                                         ¸
         – suprafetei ˆ care sunt vizibile componentelor desenate,
                  ¸ ın
         – modului de desenare.


10.2.1      Propriet˘¸ile contextului grafic
                    at
La orice tip de desenare parametrii legati de culoare, font, etc. vor fi specificati
                                        ¸                                       ¸
pentru contextul grafic ˆ care se face desenarea ¸i nu vor fi trimi¸i ca ar-
                          ın                         s                   s
gumente metodelor respective de desenare. In continuare, enumer˘m aceste
                                                                       a
propriet˘¸i ¸i metodele asociate lor din clasa Graphics.
        at s

   Proprietate                          Metode
   Culoarea de desenare                 Color getColor()
                                        void setColor(Color c)
   Fontul de scriere a textelor         Font getFont()
                                        void setFont(Font f)
   Originea coordonatelor               translate(int x, int y)
   Zona de decupare                     Shape getClip()
   (zona ˆ care sunt vizibile desenele) void setClip(Shape s)
         ın
   Modul de desenare                    void setXorMode(Color c)
                                        void setPaintMode(Color c)


10.2.2      Primitive grafice
Prin primitive grafice ne vom referi ˆ continuare la metodele clasei Graphics,
                                    ın
care permit desenarea de figuri geometrice ¸i texte.
                                            s
    Desenarea textelor se face cu uzual cu metoda drawString care prime¸te s
ca argumente un ¸ir ¸i coltul din stˆnga-jos al textului. Textul va fi desenat
                  s s     ¸          a
cu fontul ¸i culoarea curente ale contextului grafic.
          s

  // Desenam la coordonatele x=10, y=20;
  drawString("Hello", 10, 20);

   Desenarea figurilor geometrice se realizeaz˘ cu urm˘toarele metode:
                                             a       a
276                                        CAPITOLUL 10. DESENAREA

                  Figur˘ geometric˘
                        a         a       Metode
                  Linie                   drawLine
                                          drawPolyline
                  Dreptunghi simplu       drawRect
                                          fillRect
                                          clearRect
                  Dreptunghi cu chenar    draw3DRect
                  ”ridicat” sau ”adˆncit” fill3DRect
                                   a
                  Dreptunghi cu colturi
                                    ¸     drawRoundRect
                  retunjite               fillRoundRect
                  Poligon                 drawPolygon
                                          fillPolygon
                  Oval (Elips˘a           drawOval
                                          fillOval
                  Arc circular sau        drawArc
                  eliptic                 fillArc

   Metodele care ˆ ıncep cu ”fill” vor desena figuri geometrice care au interi-
orul colorat, adic˘ ”umplut” cu culoarea curent˘ a contextului de desenare,
                  a                             a
ˆ timp ce metodele care ˆ
ın                         ıncep cu ”draw” vor desena doar conturul figurii
respective.


10.3      Folosirea fonturilor
Dup˘ cum vazut, pentru a scrie un text pe ecran avem dou˘ posibilit˘¸i.
     a                                                           a          at
Prima dintre acestea este s˘ folosim o component˘ orientat˘-text, cum ar
                            a                        a         a
fi Label, iar a doua s˘ apel˘m la metodele clasei Graphics de desenare
                       a      a
a textelor, cum ar fi drawString. Indiferent de modalitatea aleas˘, putem
                                                                     a
specifica prin intermediul fonturilor cum s˘ arate textul respectiv, acest lucru
                                          a
realizˆndu-se prin metoda setFont fie din clasa Component, fie din Graphics.
      a
    Cei mai importanti parametri ce caracterizeaz˘ un font sunt:
                     ¸                             a
   • Numele fontului: Helvetica Bold, Arial Bold Italic, etc.

   • Familia din care face parte fontul: Helvetica, Arial, etc.

   • Dimensiunea fontului: ˆ altimea sa;
                           ın˘ ¸
   • Stilul fontului: ˆ
                      ıngro¸at (bold), ˆ
                           s           ınclinat (italic);
10.3. FOLOSIREA FONTURILOR                                                  277

   • Metrica fontului.
Clasele care ofer˘ suport pentru lucrul cu fonturi sunt Font ¸i FontMetrics,
                 a                                           s
ˆ continuare fiind prezentate modalit˘¸ile de lucru cu acestea.
ın                                     at

10.3.1     Clasa Font
Un obiect de tip Font ˆ  ıncapsuleaz˘ informatii despre toti parametrii unui
                                    a        ¸            ¸
font, mai putin despre metrica acestuia. Constructorul uzual al clasei este
             ¸
cel care prime¸te ca argument numele fontului, dimensiunea ¸i stilul acestuia:
               s                                            s
  Font(String name, int style, int size)
Stilul unui font este specificat prin intermediul constantelor: Font.PLAIN,
Font.BOLD, Font.ITALIC iar dimensiunea printr-un ˆ   ıntreg, ca ˆ exemplele
                                                                ın
de mai jos:
  new Font("Dialog", Font.PLAIN, 12);
  new Font("Arial", Font.ITALIC, 14);
  new Font("Courier", Font.BOLD, 10);
   Folosirea unui obiect de tip Font se realizeaz˘ uzual astfel:
                                                 a
// Pentru componente etichetate
  Label label = new Label("Un text");
  label.setFont(new Font("Dialog", Font.PLAIN, 12));

// In metoda paint(Graphics g)
  g.setFont(new Font("Courier", Font.BOLD, 10));
  g.drawString("Alt text", 10, 20);
   O platform˘ de lucru are instalate, la un moment dat, o serie ˆ
               a                                                    ıntreag˘ de
                                                                           a
fonturi care sunt disponibile pentru scrierea textelor. Lista acestor fonturi se
poate obtine astfel:
         ¸
  Font[] fonturi = GraphicsEnvironment.
    getLocalGraphicsEnvironment().getAllFonts();
   Exemplul urmator afi¸eaz˘ lista tuturor fonturilor disponibile pe plat-
                         s a
forma curent˘ de lucru. Textul fiec˘rui nume de font va fi scris cu fontul s˘u
            a                     a                                       a
corespunz˘tor.
         a
278                                                      CAPITOLUL 10. DESENAREA

                               Listing 10.3: Lucrul cu fonturi
import java . awt .*;

class Fonturi extends Canvas {
  private Font [] fonturi ;
  Dimension canvasSize = new Dimension (400 , 400) ;

    public Fonturi () {
      fonturi = Graphi c sE n v ir o nm e n t .
        g e t L o c a l G r a p h i c s E n v i r o n m e n t () . getAllFonts () ;
      canvasSize . height = (1 + fonturi . length ) * 20;
    }

    public void paint ( Graphics g ) {
      String nume ;
      for ( int i =0; i < fonturi . length ; i ++) {
        nume = fonturi [ i ]. getFontName () ;
        g . setFont ( new Font ( nume , Font . PLAIN , 14) ) ;
        g . drawString ( i + " . " + nume , 20 , ( i + 1) * 20) ;
      }
    }

    public Dimension getPreferredSize () {
      return canvasSize ;
    }
}

class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    ScrollPane sp = new ScrollPane () ;
    sp . setSize (400 , 400) ;
    sp . add ( new Fonturi () ) ;
    add ( sp , BorderLayout . CENTER ) ;
    pack () ;
  }
}

public class TestAllFonts {
  public static void main ( String args []) {
    new Fereastra ( " All fonts " ) . show () ;
  }
}
10.3. FOLOSIREA FONTURILOR                                                    279

10.3.2      Clasa FontMetrics
La afi¸area unui ¸ir cu metoda drawString trebuie s˘ specific˘m pozitia la
        s            s                                  a         a        ¸
care s˘ apar˘ textul respectiv pe ecran. In momentul ˆ care avem de afi¸at
        a       a                                        ın                   s
mai multe ¸iruri consecutiv, sau unele sub altele, trebuie s˘ calcul˘m pozitiile
              s                                              a      a        ¸
lor de afi¸are ˆ functie de lungimea ¸i ˆ ¸imea ˆ pixeli a celorlalte texte.
            s     ın     ¸              s ınalt      ın
Pentru aceasta este folosit˘ clasa FontMetrics. Un obiect din aceast˘ clas˘
                             a                                           a       a
se construie¸te pornind de la un obiect de tip Font ¸i pune la dispozitie
                s                                         s                    ¸
informatii despre dimensiunile ˆ pixeli pe care le au caracterele fontului
          ¸                        ın
respectiv ˆ  ıntr-un anumit context de desenare.
    A¸adar, un obiect de tip FontMetrics ˆ
       s                                        ıncapsuleaz˘ informatii despre
                                                            a         ¸
metrica unui font, cu alte cuvinte despre dimensiunile ˆ pixeli ale carac-
                                                            ın
terelor sale. Utilitatea principal˘ a acestei clase const˘ ˆ faptul c˘ permite
                                   a                     a ın         a
pozitionarea precis˘ a textelor pe o suprafat˘ de desenare, indiferent de fontul
     ¸                 a                     ¸a
folosit de acestea.
    Metrica unui font const˘ ˆ urm˘toarele atribute pe care le au caracterele
                              a ın    a
sale:

   • Linia de baz˘: este linia dup˘ care sunt aliniate caracterele unui font;
                 a                a

   • Linia de ascendent˘: linia superioara pe care nu o depaseste nici un
                       ¸a
     caracter din font

   • Linia de descendent˘: linia inferioar˘ sub care nu coboar˘ nici un car-
                       ¸a                 a                   a
     acter din font;

   • Ascendentul: distanta ˆ
                        ¸ ıntre linia de baz˘ ¸i linia de ascendent˘;
                                            as                    ¸a

   • Descendentul: distanta ˆ
                         ¸ ıntre linia de baz˘ ¸i linia de descendent˘;
                                             as                     ¸a

   • L˘¸imea: l˘¸imea unui anumit caracter din font;
      at       at

   • Distanta ˆ
            ¸ ıntre linii (”leading”): distanta optim˘ ˆ
                                             ¸       a ıntre dou˘ linii de text
                                                                a
     scrise cu acela¸i font.
                    s

   • In˘ltimea: distanta dintre liniile de baz˘ (leading+ascent+descent);
       a¸             ¸                       a

   Figura de mai jos prezint˘ o imagine reprezentativ˘ asupra metricii unui
                            a                        a
font:
280                                        CAPITOLUL 10. DESENAREA




   Reamintim c˘ la metoda drawString(String s, int x, int y) argu-
                  a
mentele x ¸i y repreznit˘ coltul din stˆnga-jos al textului. Ca s˘ fim mai
            s            a     ¸          a                           a
preci¸i, y reprezint˘ pozitia liniei de baz˘ a textului care va fi scris.
     s              a     ¸                 a
   Un context grafic pune la dispozitie o metod˘ special˘ getFontMetrics
                                        ¸          a         a
de creare a unui obiect de tip FontMetrics, pornind de la fontul curent al
contextului grafic:

  public void paint(Graphics g) {
    Font f = new Font("Arial", Font.BOLD, 11);
    FontMetrics fm = g.getFontMetrics();
  }

   Cele mai folosite metode ale clasei FontMetrics sunt:

   • getHeight - determin˘ ˆ altimea unei linii pe care vor fi scrise caractere
                         a ın˘ ¸
     ale unui font;

   • stringWidth - determin˘ l˘¸imea total˘ ˆ pixeli a unui ¸ir de caractere
                           a at           a ın              s
     specificat;

   • charWidth - determin˘ l˘¸imea unui anumit caracter din font.
                         a at

   In exemplul urm˘tor sunt afi¸ate pe ecran zilele s˘pt˘mˆnii ¸i lunile an-
                  a           s                     a a a s
ului:
10.3. FOLOSIREA FONTURILOR                                            281

                  Listing 10.4: Folosirea clasei FontMetrics
import java . awt .*;

class Texte extends Canvas {
  Dimension canvasSize = new Dimension (800 , 100) ;
  private String [] zile = {
    " Luni " , " Marti " , " Miercuri " , " Joi " ,
    " Vineri " , " Sambata " , " Duminica " };
  private String [] luni = {
    " Ianuarie " , " Februarie " , " Martie " , " Aprilie " ,
    " Mai " , " Iunie " , " Iulie " , " August " , " Septembrie " ,
    " Octombrie " , " Noiembrie " , " Decembrie " };

  public void paint ( Graphics g ) {
    FontMetrics fm ;
    int x , y ;
    String etZile = " Zilele saptamanii : " ,
              etLuni = " Lunile anului : " , text ;

     // Alegem un font si aflam metrica sa
     g . setFont ( new Font ( " Arial " , Font . BOLD , 20) ) ;
     fm = g . getFontMetrics () ;
     x = 0;
     y = fm . getHeight () ;
     g . drawString ( etZile , x , y ) ;
     x += fm . stringWidth ( etZile ) ;

     for ( int i =0; i < zile . length ; i ++) {
         text = zile [ i ];
         if ( i < zile . length - 1)
             text += " , " ;
         g . drawString ( text , x , y ) ;
         x += fm . stringWidth ( text ) ;
     }
     // Schimbam fontul
     g . setFont ( new Font ( " Dialog " , Font . PLAIN , 14) ) ;
     fm = g . getFontMetrics () ;
     x = 0;
     y += fm . getHeight () ;
     g . drawString ( etLuni , x , y ) ;
     x += fm . stringWidth ( etLuni ) ;

     for ( int i =0; i < luni . length ; i ++) {
       text = luni [ i ];
       if ( i < luni . length - 1)
282                                         CAPITOLUL 10. DESENAREA

            text += " , " ;
        g . drawString ( text , x , y ) ;
        x += fm . stringWidth ( text ) ;
      }
    }
    public Dimension getPreferredSize () {
      return canvasSize ;
    }
}

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




10.4       Folosirea culorilor
Orice culoare este format˘ prin combinatia culorilor standard ro¸u (red),
                             a                ¸                       s
verde (green) ¸i albastru (blue), la care se adaug˘ un anumit grad de transparent˘
                s                                 a                             ¸a
(alpha). Fiecare din ace¸ti patru parametri poate varia ˆ
                             s                                 ıntr-un interval
cuprins fie ˆ ıntre 0 ¸i 255 (dac˘ dorim s˘ specific˘m valorile prin numere
                      s            a          a       a
ˆ
ıntregi), fie ˆıntre 0.0 ¸i 1.0 (dac˘ dorim s˘ specific˘m valorile prin numere
                        s           a          a       a
reale).
    O culoare este reprezentat˘ printr-o instant˘ a clasei Color sau a sub-
                                 a                ¸a
clasei sale SystemColor. Pentru a crea o culoare avem dou˘ posibilit˘¸i:
                                                                a         at

    • S˘ folosim una din constantele definite ˆ cele dou˘ clase;
       a                                     ın        a

    • S˘ folosim unul din constructorii clasei Color.
       a

    S˘ vedem mai ˆ ai care sunt constantele definite ˆ aceste clase:
     a           ıntˆ                               ın
10.4. FOLOSIREA CULORILOR                                                   283

                      Color        SystemColor
                      black        activeCaption
                      blue         activeCaptionBorder
                      cyan         activeCaptionText
                      darkGray     control
                      gray         controlHighlight
                      green        controlShadow
                      lightGray    contolText
                      magenta      desktop
                      orange       menu
                      pink         text
                      red          textHighlight
                      white        window
                      yellow       ...
    Observati c˘ ˆ clasa Color sunt definite culori uzuale din paleta standard
             ¸ a ın
de culori, ˆ timp ce ˆ clasa SystemColor sunt definite culorile componen-
           ın         ın
telor standard (ferestre, texte, meniuri, etc) ale platformei curente de lucru.
Folosirea acestor constante se face ca ˆ exemplele de mai jos:
                                       ın
  Color rosu = Color.red;
  Color galben = Color.yellow;
  Color fundal = SystemColor.desktop;
Dac˘ nici una din aceste culori predefinite nu corespunde preferintelor noas-
     a                                                               ¸
tre, atunci putem crea noi culori prin intermediul constructorilor clasei Color:
  Color(float red, flot green, float blue)
  Color(flot red, float green, float blue, float alpha)
  Color(int red, int green, int blue)
  Color(int red, int green, int blue, int alpha)
  Color(int rgb)
unde red, green, blue, alpha sunt valorile pentru ro¸u, verde, albastru ¸i
                                                        s                   s
transparent˘ iar parametrul ”rgb” de la ultimul constructor reprezint˘ un
            ¸a                                                          a
ˆ
ıntreg format din: bitii 16-23 ro¸u, 8-15 verde, 0-7 albastru.
                     ¸           s
                                 a ıntre 0 − 255 pentru tipul int, respectiv
    Valorile argumentelor variaz˘ ˆ
0.0 − 1.0 pentru tipul float. Valoarea 255 (sau 1.0) pentru transparent˘   ¸a
specific˘ faptul c˘ respectiva culoare este complet opac˘, iar valoarea 0 (sau
        a        a                                       a
0.0) specific˘ transparent˘ total˘. Implicit, culorile sunt complet opace.
             a            ¸a     a
284                                         CAPITOLUL 10. DESENAREA

  // Exemple de folosire a constructorilor:
  Color alb = new Color(255, 255, 255);
  Color negru = new Color(0, 0, 0);
  Color rosu = new Color(255, 0, 0);
  Color rosuTransparent = new Color(255, 0, 0, 128);

   Metodele cele mai folosite ale clasei Color sunt:

      brighter    Creeaz˘ o noua versiune a culorii curente
                        a
      darker      mai deschis˘, respectiv mai ˆ
                             a                ınchis˘
                                                    a
      getRed
      getGreen    Determin˘ parametrii din care
                            a
      getBlue     este alcatuit˘ culoarea
                               a
      getAlpha
      getRGB      Determin˘ valoarea ce reprezint˘ culoarea
                           a                        a
                  respectiv˘ (bitii 16-23 ro¸u, 8-15 verde, 0-7 albastru)
                           a ¸              s

    S˘ consider˘m o aplicatie cu ajutorul c˘reia putem vizualiza dinamic cu-
      a         a            ¸                a
lorile obtinute prin diferite combinatii ale parametrilor ce formeaz˘ o culoare.
          ¸                          ¸                              a
Aplicatia va ar˘ta astfel:
        ¸       a




                     Listing 10.5: Folosirea clasei Color
import java . awt .*;
import java . awt . event .*;

class Culoare extends Canvas {
  public Color color = new Color (0 , 0 , 0 , 255) ;
  Dimension canvasSize = new Dimension (150 , 50) ;

  public void paint ( Graphics g ) {
    g . setColor ( Color . black ) ;
10.4. FOLOSIREA CULORILOR                                                    285

        g . setFont ( new Font ( " Arial " , Font . BOLD , 12) ) ;
        String text = " " ;
        text += " R = " + color . getRed () ;
        text += " G = " + color . getGreen () ;
        text += " B = " + color . getBlue () ;
        text += " A = " + color . getAlpha () ;
        g . drawString ( text , 0 , 30) ;

        g . setColor ( color ) ;
        g . fillRect (0 , 0 , canvasSize . width , canvasSize . height ) ;
    }

    public Dimension getPreferredSize () {
      return canvasSize ;
    }
}

class Fereastra extends Frame implements Ad ju st me nt Lis te ne r {
  private Scrollbar rValue , gValue , bValue , aValue ;
  private Culoare culoare ;

    public Fereastra ( String titlu ) {
      super ( titlu ) ;
      this . addWindowListener ( new WindowAdapter () {
         public void windowClosing ( WindowEvent e ) {
           System . exit (0) ;
         }
      }) ;

        Panel rgbValues = new Panel () ;
        rgbValues . setLayout ( new GridLayout (4 , 1) ) ;
        rValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 ,
           256) ;
        rValue . setBackground ( Color . red ) ;

        gValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 ,
           256) ;
        gValue . setBackground ( Color . green ) ;

        bValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 ,
           256) ;
        bValue . setBackground ( Color . blue ) ;

        aValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 ,
           256) ;
286                                                    CAPITOLUL 10. DESENAREA

        aValue . setValue (255) ;
        aValue . setBackground ( Color . lightGray ) ;

        rgbValues . add ( rValue ) ;
        rgbValues . add ( gValue ) ;
        rgbValues . add ( bValue ) ;
        rgbValues . add ( aValue ) ;
        rgbValues . setSize (200 , 100) ;
        add ( rgbValues , BorderLayout . CENTER ) ;

        culoare = new Culoare () ;
        add ( culoare , BorderLayout . NORTH ) ;

        pack () ;

        rValue . add Adjus t m e n t L i s t e n e r ( this ) ;
        gValue . add Adjus t m e n t L i s t e n e r ( this ) ;
        bValue . add Adjus t m e n t L i s t e n e r ( this ) ;
        aValue . add Adjus t m e n t L i s t e n e r ( this ) ;
    }

    public void ad ju stm en tV al u e C h an ge d ( AdjustmentEvent e ) {
      int r = rValue . getValue () ;
      int g = gValue . getValue () ;
      int b = bValue . getValue () ;
      int a = aValue . getValue () ;
      Color c = new Color (r , g , b , a ) ;
      culoare . color = c ;
      culoare . repaint () ;
    }
}

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




10.5           Folosirea imaginilor
Aceasta este o imagine:
10.5. FOLOSIREA IMAGINILOR                                               287




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


10.5.1     Afi¸area imaginilor
             s
Afi¸area unei imagini presupune realizarea urm˘toarilor doi pa¸i:
  s                                          a               s

   1. Crearea unui obiect de tip Image;

   2. Afi¸area propriu-zis˘ ˆ
        s                a ıntr-un context grafic;

   Crearea unui obiect de tip Image se face folosind o imagine dintr-un fi¸ier
                                                                          s
fie aflat pe ma¸ina pe care se lucreaz˘, fie aflat la o anumit˘ adres˘ Web
               s                      a                       a       a
(URL). Metodele pentru ˆ arcarea unei imagini dintr-un fi¸ier se g˘sesc
                          ınc˘                                 s        a
ˆ clasele Applet ¸i Toolkit, avˆnd ˆ a aceea¸i denumire getImage ¸i
ın                s               a    ıns˘        s                        s
urm˘toarele formate:
    a

    Applet                           Toolkit
    getImage(URL url)                getImage(URL url)
    getImage(URL url, String fisier) getImage(String fisier)

    Pentru a obtine un obiect de tip Toolkit se va folosi metoda getDefaultToolkit,
               ¸
ca ˆ exemplul de mai jos:
   ın

  Toolkit toolkit = Toolkit.getDefaultToolkit();
  Image image1 = toolkit.getImage("poza.gif");
  Image image2 = toolkit.getImage(
    new URL("http://www.infoiasi.ro/~acf/poza.gif"));
288                                        CAPITOLUL 10. DESENAREA

    Metoda getImage nu verific˘ dac˘ fi¸ierul sau adresa specificata reprezint˘
                                 a     a s                                    a
o imagine valid˘ ¸i nici nu ˆ
                as          ıncarc˘ efectiv imaginea ˆ memorie, aceste operatiuni
                                   a                 ın                     ¸
fiind f˘cute abia ˆ momentul ˆ care se va realiza afi¸area imaginii pentru
       a           ın             ın                     s
prima dat˘. Metoda nu face decˆt s˘ creeze un obiect de tip Image care face
           a                        a a
referint˘ la o anumit˘ imagine extern˘.
       ¸a             a                  a


   Afi¸area unei imagini ˆ
      s                 ıntr-un context grafic se realizeaz˘ prin intermediul
                                                          a
metodei drawImage din clasa Graphics ¸i, ˆ general, va fi facut˘ ˆ metoda
                                        s ın                     a ın
paint a unei componente. Cele mai uzuale formate ale metodei sunt:
  boolean drawImage(Image img, int         x, int y, ImageObserver observer)
  boolean drawImage(Image img, int         x, int y, Color bgcolor,
      ImageObserver observer)
  boolean drawImage(Image img, int         x, int y, int width, int height,
      ImageObserver observer)
  boolean drawImage(Image img, int         x, int y, int width, int height,
      Color bgcolor, ImageObserver         observer)
unde:
   • img este obiectul ce reprezint˘ imaginea;
                                   a
   • x, y sunt coordonatele stˆnga-sus la care va fi afi¸at˘ imaginea, relative
                              a                       s a
     la spatiul de coordonate al contextului grafic;
            ¸
   • observer este un obiect care ”observ˘” ˆ arcarea imaginii ¸i va fi in-
                                          a ınc˘               s
     format pe m˘sura derul˘rii acesteia;
                 a          a
   • width, heigth reprezint˘ ˆ ¸imea ¸i l˘¸imea la care trebuie scalat˘
                            a ınalt      s at                              a
     imaginea (dac˘ lipsesc, imaginea va fi afi¸at˘ la dimensiunile ei reale);
                   a                         s a
   • bgColor reprezint˘ culoarea cu care vor fi colorati pixelii transparenti
                       a                             ¸                    ¸
     ai imaginii (poate s˘ lipseasc˘).
                         a         a
    In exemplul urm˘tor afi¸am aceea¸i imagine de trei ori, folosind forme
                     a      s      s
diferite ale metodei drawImage:
  Image img = Toolkit.getDefaultToolkit().getImage("taz.gif");
  g.drawImage(img, 0, 0, this);
  g.drawImage(img, 0, 200, 100, 100, this);
  g.drawImage(img, 200, 0, 200, 400, Color.yellow, this);
10.5. FOLOSIREA IMAGINILOR                                                        289

    Metoda drawImage returneaz˘ true dac˘ imaginea a fost afi¸at˘ ˆ ˆ
                                a         a                  s a ın ıntregime
¸i false ˆ caz contrar, cu alte cuvinte metoda nu a¸tept˘ ca o imagine s˘
s        ın                                         s    a                a
fie complet afi¸at˘ ci se termin˘ imediat ce procesul de afi¸are a ˆ
             s a              a                          s      ınceput. In
sectiunea urm˘toare vom detalia acest aspect.
   ¸         a


10.5.2      Monitorizarea ˆ arc˘rii imaginilor
                          ınc˘ a
In cazul ˆ care se afi¸eaz˘ o imagine care se g˘se¸te pe Internet sau imaginea
          ın             s a                     a s
afi¸ata este de dimensiuni mari se va observa c˘ aceasta nu apare complet de
   s                                              a
la ˆ
   ınceput ci este desenat˘ treptat, f˘r˘ interventia programatorului. Acest
                               a          aa          ¸
lucru se ˆ ampl˘ deoarece metoda drawImage nu face decˆt s˘ declan¸eze
           ıntˆ     a                                              a a           s
procesul de ˆ arcare ¸i desenare a imaginii, dup˘ care red˘ imediat con-
               ınc˘        s                            a           a
trolul apelantului, lucru deosebit de util ˆ   ıntrucˆt procesul de ˆ arcare a
                                                      a                  ınc˘
unei imagini poate dura mult ¸i nu este de dorit ca ˆ acest interval de timp
                                   s                      ın
(pˆn˘ la ˆ arcarea complet˘ a imaginii) aplicatia s˘ fie blocat˘. Ca urmare,
   a a ınc˘                      a                 ¸ a               a
la apelul metodei drawImage va fi desenat˘ numai portiunea de imagine care
                                             a               ¸
este disponibil˘ la momentul initial ¸i care poate fi incomplet˘. De aceea
                 a                     ¸ s                             a
trebuie s˘ existe un mecanism prin care componenta s˘ fie redesenat˘ au-
           a                                                   a               a
tomat ˆ momentul ˆ care au mai sosit informatii legate de imagine, pˆn˘ la
        ın              ın                          ¸                         a a
afi¸area sa complet˘. Acest mecanism este realizat prin intermediul interfetei
   s                  a                                                            ¸
ImageObserver, implementat˘ de clasa Component ¸i deci de toate compo-
                                     a                      s
nentele. Aceast˘ interfat˘ descrie obiecte care au ˆ
                  a          ¸a                       ınceput s˘ utilizeze o imag-
                                                                 a
ine incomplet˘ ¸i care trebuie anuntate de noile date obtinute ˆ legatur˘ cu
                as                      ¸                      ¸     ın         a
imaginea respectiv˘.  a
     Interfata ImageObserver are o singur˘ metod˘ numit˘ imageUpdate,
             ¸                               a          a        a
ce va fi apelat˘ periodic de firul de executie (creat automat) care se ocup˘
                 a                           ¸                                       a
cu ˆ arcarea imaginii. Formatul acestei metode este:
    ınc˘


  boolean imageUpdate (Image img, int flags,
                       int x, int y, int w, int h )

    Implementarea implicit˘ const˘ dintr-un apel la metoda repaint pentru
                           a       a
dreptunghiul specificat la apel ¸i care reprezint˘ zona din imagine pentru care
                               s                a
se cunosc noi informatii. Intregul f lags furnizeaz˘ informatii despre starea
                      ¸                             a         ¸
transferului. Aceste informatii pot fi aflate prin intermediul constantelor
                              ¸
definite de interfat˘:
                  ¸a
290                                        CAPITOLUL 10. DESENAREA

          ABORT      Inc˘rcarea imaginii a fost ˆ
                         a                       ıntrerupt˘,
                                                           a
                     ˆ
                     ınainte de completarea sa
          ALLBITS    Imaginea a fost ˆıncarcat˘ complet
                                              a
          ERROR      A ap˘rut o eroare ˆ timpul
                           a             ın
                     ˆ arc˘rii imaginii
                     ınc˘ a
          FRAMEBITS  Toti bitii cadrului curent sunt disponibili
                        ¸ ¸
          HEIGHT     In˘ltimea imaginii este disponibil˘
                        a¸                               a
          PROPERTIES Propriet˘¸ile imaginii sunt disponibile
                              at
          SOMEBITS   Au fost receptionati noi pixeli ai imaginii
                                   ¸     ¸
          WIDTH      L˘¸imea imaginii este disponibil˘
                       at                              a
   Prezenta ˆ parametrul f lags a unui bit de valoare 1 pe pozitia reprezen-
          ¸ ın                                                 ¸
tata de o constant˘ ˆ
                  a ınseamn˘ c˘ respectiva conditie este ˆ
                           a a                  ¸        ındeplinit˘.
                                                                   a
// Imaginea este completa
(flags & ALLBITS) != 0

// Eroare sau transferul imaginii a fost intrerupt
(flags & ERROR | ABORT ) != 0
   Metoda imageUpdate poate fi redefint˘ de o component˘ pentru a per-
                                            a                  a
sonaliza procesul de afi¸are al imaginii. Aceasta va fi apelat˘ apelat˘ asincron
                       s                                    a       a
de fiecare dat˘ cˆnd sunt disponibili noi pixeli.
              a a
    public boolean imageUpdate(Image img, int flags,
        int x, int y, int w, int h) {
      // Desenam imaginea numai daca toti bitii sunt disponibili
      if (( flags & ALLBITS) != 0)
        repaint();

      // Daca sunt toti bitii nu mai sunt necesare noi update-uri
      return ( (flags & (ALLBITS | ABORT)) == 0);
}
   De asemenea, se observ˘ c˘ metodele clasei Image pentru determinarea
                           a a
dimensiunilor unei imagini au ca argument un obiect de tip ImageObserver.
    int getHeight(ImageObserver observer)
    int getWidth(ImageObserver observer)
   Dac˘ desenarea se face folosind clasa Canvas, atunci argumentul observer
       a
al metodelor referitoare la imagini va fi this.
10.5. FOLOSIREA IMAGINILOR                                                 291

10.5.3       Mecanismul de ”double-buffering”
Tehnica de double-buffering implic˘ realizarea unui desen ˆ memorie ¸i apoi
                                    a                      ın           s
transferul s˘u pe ecran, pentru a elimina efectul nepl˘cut de ”clipire” (”flick-
             a                                        a
ering”) rezultat atunci cˆnd sunt efectuate redesen˘ri repetate la intervale
                          a                           a
mici de timp. O situatie frecvent˘ ˆ care se apeleaz˘ la double-buffering este
                      ¸          a ın                a
crearea de animatii.
                  ¸
    Secventa general˘ de implementare a mecanismului de double-buffering
           ¸         a
este urm˘toarea:
         a
    // Supradefinim update pentru a elimina stergerea desenului
    public void update(Graphics g) {
      paint(g);
    }

    public void paint(Graphics g) {
      // Desenam in memorie pe un obiect de tip Image
      // w si h sunt dimensiunile desenului
      Image img = createImage(w, h);
      Graphics gmem = img.getGraphics();

        /* Realizam desenul folosind gmem
          gmem.setColor(...);
          gmem.fillOval(...);
          ...
        */

        // Transferam desenul din memorie pe ecran
        // desenand de fapt imaginea creata
        g.drawImage(img, 0, 0, this);

        gmem.dispose();
    }
}

10.5.4       Salvarea desenelor ˆ format JPEG
                                ın
Pachetul com.sun.image.codec.jpeg din distributia standard Java ofer˘
                                               ¸                       a
suport pentru salvarea unei imagini aflate ˆ memorie ˆ
                                          ın        ıntr-un fi¸ier ˆ for-
                                                             s ın
292                                      CAPITOLUL 10. DESENAREA

mat JPEG. O clas˘ responsabil˘ cu realizarea acestei operatiuni ar putea fi
                a            a                            ¸
definit˘ astfel:
      a

import    com.sun.image.codec.jpeg.*;
import    java.awt.image.BufferedImage;
import    java.awt.*;
import    java.io.*;

class JPEGWriter {

    static float quality = 0.9f; //intre 0 si 1
    public static void write(BufferedImage img, String filename) {

        try {
          FileOutputStream out = new FileOutputStream(filename);

          JPEGImageEncoder encoder =
              JPEGCodec.createJPEGEncoder(out);
          JPEGEncodeParam jep =
              encoder.getDefaultJPEGEncodeParam(img);
          jep.setQuality(quality, false);

          // Folosim setarile de codare jpeg implicite
          encoder.setJPEGEncodeParam(jep);
          encoder.encode(img);

          out.close();
        } catch( Exception e ) {
          e.printStackTrace();
        }
    }
}


10.5.5       Crearea imaginilor ˆ memorie
                                ın
In cazul ˆ care dorim s˘ folosim o anumit˘ imagine creat˘ direct din pro-
          ın               a                  a             a
gram ¸i nu ˆ
      s      ıncarcat˘ dintr-un fi¸ier vom folosi clasa MemoryImageSource,
                     a           s
aflata ˆ pachetul java.awt.image. Pentru aceasta va trebui s˘ definim un
       ın                                                      a
˘
10.6. TIPARIREA                                                           293

vector de numere ˆ   ıntregi ˆ care vom scrie valorile ˆ
                             ın                        ıntregi (RGB) ale cu-
lorilor pixelilor ce definesc imaginea noastr˘. Dimensiunea vectorului va
                                             a
fi ˆ altimea ˆ
   ın˘ ¸      ınmultit˘ cu l˘¸imea ˆ pixeli a imaginii. Constructorul clasei
                    ¸ a      at     ın
MemoryImageSource este:
  MemoryImageSource(int w, int h, int[] pixeli, int off, int scan)
unde:
   • w, h reprezint˘ dimensiunile imaginii (l˘¸imea ¸i ˆ altimea);
                   a                         at     s ın˘ ¸
   • pixeli[] este vectorul cu culorile imaginii;
   • of f, scan reprezint˘ modalitatea de construire a matricii imaginii pornind
                         a
     de la vectorul cu pixeli, normal aceste valori sunt off = 0, scan = w
   In exemplul urmator vom crea o imagine cu pixeli de culori aleatorii ¸i o
                                                                        s
vom afi¸a pe ecran:
       s
  int w = 100;
  int h = 100;
  int[] pix = new int[w * h];
  int index = 0;
  for (int y = 0; y < h; y++) {
  for (int x = 0; x < w; x++) {
    int red = (int) (Math.random() * 255);
    int green = (int) (Math.random() * 255);
    int blue = (int) (Math.random() * 255);
    pix[index++] = new Color(red, green, blue).getRGB();
  }

  img = createImage(new MemoryImageSource(w, h, pix, 0, w));
  g.drawImage(img, 0, 0, this); // g este un context grafic


10.6      Tip˘rirea
             a
Tip˘rirea ˆ Java este tratat˘ ˆ aceea¸i manier˘ ca ¸i desenarea, singurul
    a      ın                a ın        s       a   s
lucru diferit fiind contextul grafic ˆ care se execut˘ operatiile. Pachetul
                                    ın              a       ¸
care ofer˘ suport pentru tip˘rire este java.awt.print, iar clasa principal˘
         a                   a                                            a
care controleaz˘ procesul de tip˘rire este PrinterJob. O aplicatie va apela
                a                a                             ¸
metode ale acestei clase pentru:
294                                           CAPITOLUL 10. DESENAREA

   • Crearea unei sesiuni de tip˘rire (job);
                                a

   • Invocarea dialogului cu utilizatorul pentru specificarea unor parametri
     legati de tip˘rire;
         ¸        a

   • Tip˘rirea efectiv˘.
        a             a

    Orice component˘ care poate fi afi¸at˘ pe ecran poate fi ¸i tip˘rit˘. In gen-
                      a                 s a                    s     a a
eral, orice informatii care trebuie atˆt afi¸ate cˆt ¸i tip˘rite, vor fi ˆ
                   ¸                  a      s   a s      a            ıncapsulate
ˆ
ıntr-un obiect grafic - component˘, care are o reprezentare vizual˘ descris˘
                                    a                                    a        a
de metoda paint ¸i care va specifica ¸i modalitatea de reprezentare a sa la
                   s                      s
imprimant˘. a
    Un obiect care va fi tip˘rit trebuie s˘ implementeze interfata Printable,
                             a              a                      ¸
care contine o singur˘ metod˘ print, responsabil˘ cu descrierea modalit˘¸ii
          ¸            a        a                    a                         at
de tip˘rire a obiectului. In cazul cˆnd imaginea de pe ecran coincide cu
       a                                a
imaginea de la imprimant˘, metodele paint ¸i print pot specifica aceea¸i
                             a                   s                               s
secvent˘ de cod. In general, metoda print are urm˘torul format:
       ¸a                                               a

  public int print(Graphics g, PageFormat pf, int pageIndex)
throws PrinterException {

      // Descrirea imaginii obiectului ce va fi afisata la imprimanta
      // Poate fi un apel la metoda paint: paint(g)

      if (ceva nu este in regula}) {
        return Printable.NO_SUCH_PAGE;
      }

      return Printable.PAGE_EXISTS;
  }

    Pa¸ii care trebuie efectuati pentru tip˘rirea unui obiect sunt:
      s                       ¸            a

   1. Crearea unei sesiuni de tip˘rire: PrinterJob.getPrinterJob
                                 a

   2. Specificarea obiectului care va fi tip˘rit: setPrintable; acesta trebuie
                                          a
      s˘ implementeze interfata Printable;
       a                      ¸

   3. Optional, initierea unui dialog cu utilizatorul pentru precizarea unor
         ¸         ¸
      parametri legati de tip˘rire: printDialog;
                     ¸       a
˘
10.6. TIPARIREA                                                        295

    4. Tip˘rirea efectiv˘: print.
          a             a

   In exemplul urm˘tor vom defini un obiect care are aceea¸i reprezentare
                      a                                    s
pe ecran cˆt ¸i la imprimant˘ (un cerc circumscris unui p˘trat, ˆ
           a s                a                          a      ınsotite de
un text) ¸i vom tip˘ri obiectul respectiv.
         s          a

                    Listing 10.6: Tip˘rirea unei componente
                                     a
import     java . io .*;
import     java . awt .*;
import     java . awt . event .*;
import     java . awt . print .*;

class Plansa extends Canvas implements Printable {
  Dimension d = new Dimension (400 , 400) ;
  public Dimension getPreferredSize () {
    return d ;
  }

    public void paint ( Graphics g ) {
      g . drawRect (200 , 200 , 100 , 100) ;
      g . drawOval (200 , 200 , 100 , 100) ;
      g . drawString ( " Hello " , 200 , 200) ;
    }

    public int print ( Graphics g , PageFormat pf , int pi )
        throws PrinterException {
      if ( pi >= 1)
        return Printable . NO_SUCH_PAGE ;

        paint ( g ) ;
        g . drawString ( " Numai la imprimanta " , 200 , 300) ;

        return Printable . PAGE_EXISTS ;
    }
}

class Fereastra extends Frame implements ActionListener {
  private Plansa plansa = new Plansa () ;
  private Button print = new Button ( " Print " ) ;

    public Fereastra ( String titlu ) {
      super ( titlu ) ;
      addWindowListener ( new WindowAdapter () {
        public void windowClosing ( WindowEvent e ) {
296                                             CAPITOLUL 10. DESENAREA

               System . exit (0) ;
           }
        }) ;

        add ( plansa , BorderLayout . CENTER ) ;

        Panel south = new Panel () ;
        south . setLayout ( new FlowLayout ( FlowLayout . CENTER ) ) ;
        south . add ( print ) ;
        add ( south , BorderLayout . SOUTH ) ;

        print . addActionListe ner ( this ) ;
        pack () ;
    }

    public void actionPerformed ( ActionEvent e ) {
      // 1. Crearea unei sesiuni de tiparire
      PrinterJob printJob = PrinterJob . getPrinterJob () ;

        // 2. Stabilirea obiectului ce va fi tiparit
        printJob . setPrintable ( plansa ) ;

        // 3. Initierea dialogului cu utilizatorul
        if ( printJob . printDialog () ) {
          try {
             // 4. Tiparirea efectiva
             printJob . print () ;
          } catch ( PrinterException ex ) {
             System . out . println ( " Exceptie la tiparire ! " ) ;
             ex . printStackTrace () ;
          }
        }
    }
}

public class TestPrint {
  public static void main ( String args []) throws Exception {
    Fereastra f = new Fereastra ( " Test Print " ) ;
    f . show () ;
  }
}
˘
10.6. TIPARIREA                                                            297

    Tiparirea textelor
O alt˘ variant˘ pentru tip˘rirea de texte este deschiderea unui flux c˘tre
      a        a             a                                            a
dispozitivul special reprezentat de imprimant˘ ¸i scrierea informatiilor, linie
                                             as                     ¸
cu linie, pe acest flux. In sistemul de operare Windows, imprimanta poate
fi referit˘ prin ”lpt1”, iar ˆ Unix prin ”/dev/lp”. Observati c˘ aceast˘
         a                   ın                                  ¸ a          a
abordare nu este portabil˘, deoarece necesit˘ tratare special˘ ˆ functie de
                           a                a                  a ın     ¸
sistemul de operare folosit.

                         Listing 10.7: Tip˘rirea textelor
                                          a
import java . io .*;
import java . awt .*;

class TestPrintText {
  public static void main ( String args []) throws Exception {
    // pentru Windows
    PrintWriter imp = new PrintWriter ( new FileWriter ( " lpt1 " ) )
       ;

        // pentru UNIX
        // PrintWriter imp = new PrintWriter ( new FileWriter ("/ dev /
            lp ") ) ;

        imp . println ( " Test imprimanta " ) ;
        imp . println ( " ABCDE " ) ;
        imp . close () ;
    }
}
298   CAPITOLUL 10. DESENAREA
Capitolul 11

Swing

11.1      Introducere
11.1.1     JFC
Tehnologia Swing face parte dintr-un proiect mai amplu numit JFC (Java
Foundation Classes) care pune la dispozitie o serie ˆ
                                          ¸         ıntreag˘ de facilit˘¸i pen-
                                                           a           at
tru scrierea de aplicatii cu o interfat˘ grafic˘ mult ˆ
                      ¸               ¸a      a       ımbog˘¸it˘ functional ¸i
                                                           at a        ¸     s
estetic fat˘ de vechiul model AWT. In JFC sunt incluse urm˘toarele:
          ¸a                                                  a
   • Componente Swing
     Sunt componente ce ˆ
                        ınlocuiesc ¸i ˆ acela¸i timp extind vechiul set oferit
                                   s ın      s
     de modelul AWT.
   • Look-and-Feel
     Permite schimbarea ˆ a¸i¸˘rii ¸i a modului de interactiune cu aplicatia
                         ınf˘t sa s                          ¸            ¸
     ˆ functie de preferintele fiec˘ruia. Acela¸i program poate utiliza di-
     ın     ¸              ¸        a            s
     verse moduri Look-and-Feel, cum ar fi cele standard Windows, Mac,
     Java, Motif sau altele oferite de diver¸i dezvoltatori, acestea putˆnd fi
                                            s                           a
     interschimbate de c˘tre utilizator chiar la momentul executiei .
                        a                                         ¸
   • Accessibility API
     Permite dezvoltarea de aplicatii care s˘ comunice cu dispozitive uti-
                                     ¸         a
     lizate de c˘tre persoane cu diverse tipuri de handicap, cum ar fi cititoare
                a
     de ecran, dispozitive de recunoa¸tere a vocii, ecrane Braille, etc.
                                       s
   • Java 2D API
     Folosind Java 2D pot fi create aplicatii care utilizeaz˘ grafic˘ la un
                                         ¸                 a      a

                                     299
300                                                  CAPITOLUL 11. SWING

      nivel avansat. Clasele puse la dispozitie permit crearea de desene com-
                                             ¸
      plexe, efectuarea de operatii geometrice (rotiri, scal˘ri, translatii, etc.),
                                ¸                           a           ¸
      prelucrarea de imagini, tip˘rire, etc.
                                 a

   • Drag-and-Drop
     Ofer˘ posibilitatea de a efectua operatii drag-and-drop ˆ
         a                                 ¸                 ıntre aplicatii
                                                                         ¸
     Java ¸i aplicatii native.
          s        ¸

   • Internationalizare
              ¸
     Internationalizarea ¸i localizarea aplicatiilor sunt dou˘ facilit˘¸i extrem
            ¸            s                    ¸              a        at
     de importante care permit dezvoltarea de aplicatii care s˘ poat˘ fi con-
                                                        ¸        a       a
     figurate pentru exploatarea lor ˆ diverse zone ale globului, utilizˆnd
                                       ın                                    a
     limba ¸i particularit˘¸ile legate de formatarea datei, numerelor sau a
           s              at
     monedei din zona respectiv˘. a

   In aceste capitol vom face o prezentare scurt˘ a componentelor Swing,
                                                   a
deoarece prezentarea detaliata a tuturor facilit˘¸ilor oferite de JFC ar oferi
                                                at
suficient material pentru un volum de sine st˘t˘tor.
                                             aa


11.1.2      Swing API
Unul din principalele deziderate ale tehnologiei Swing a fost s˘ pun˘ la
                                                                   a      a
dispozitie un set de componente GUI extensibile care s˘ permit˘ dezvoltarea
       ¸                                                a       a
rapid˘ de aplicatii Java cu interfat˘ grafic˘ competitiv˘ din punct de vedere
     a           ¸                 ¸a       a           a
comercial. Pentru a realiza acest lucru, API-ul oferit de Swing este deosebit
de complex avˆnd 17 pachete ˆ care se g˘sesc sute de clase ¸i interfete. Lista
               a               ın         a                 s        ¸
complet˘ a pacehetelor din distributia standard 1.4 este dat˘ ˆ tabelul de
         a                            ¸                       a ın
mai jos:

         javax.accessibility             javax.swing.plaf
         javax.swing.text.html           javax.swing
         javax.swing.plaf.basic          javax.swing.text.parser
         javax.swing.border              javax.swing.plaf.metal
         javax.swing.text.rtf            javax.swing.colorchooser
         javax.swing.plaf.multi          javax.swing.tree
         javax.swing.event               javax.swing.table
         javax.swing.undo                javax.swing.filechooser
         javax.swing.text
11.1. INTRODUCERE                                                           301

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


   Componentele folosite pentru crearea interfetelor grafice Swing pot fi gru-
                                               ¸
pate astfel:

   • Componente atomice
     JLabel, JButton, JCheckBox, JRadioButton, JToggleButton, JScrollBar,
     JSlider, JProgressBar, JSeparator

   • Componente complexe
     JTable, JTree, JComboBox, JSpinner, JList, JFileChooser, JColorChooser,
     JOptionPane

   • Componente pentru editare de text
     JTextField, JFormattedTextField, JPasswordField, JTextArea,
     JEditorPane, JTextPane

   • Meniuri
     JMenuBar, JMenu, JPopupMenu, JMenuItem, JCheckboxMenuItem,
     JRadioButtonMenuItem

   • Containere intermediare
     JPanel, JScrollPane, JSplitPane, JTabbedPane, JDesktopPane,
     JToolBar

   • Containere de nivel ˆ
                         ınalt
     JFrame, JDialog, JWindow, JInternalFrame, JApplet

11.1.3     Asem˘n˘ri ¸i deosebiri cu AWT
               a a s
Nu se poate spune c˘ Swing ˆ
                      a      ınlocuie¸te modelul AWT ci ˆ extinde pe acesta
                                     s                     ıl
din urm˘ ad˘ugˆndu-i noi componente care fie ˆ
         a a a                                  ınlocuiesc unele vechi fie sunt
cu totul noi. O conventie ˆ general respectat˘ este prefixarea numelui unei
                        ¸ ın                   a
clase AWT cu litera ”J” pentru a denumi clasa corespondent˘ din Swing.
                                                                 a
Astfel, ˆ locul clasei java.awt.Button putem folosi javax.swing.JButton,
        ın
ˆ loc de java.awt.Label putem folosi javax.swing.JLabel, etc. Este reco-
ın
mandat ca o aplicatie cu interfat˘ grafic˘ s˘ foloseasc˘ fie componente AWT,
                    ¸           ¸a       a a          a
fie Swing, amestecarea lor fiind mai putin uzual˘.
                                        ¸        a
302                                              CAPITOLUL 11. SWING

    Aplicatiile GUI vor avea ˆ continuare nevoie de pachetul java.awt deoarece
           ¸                 ın
aici sunt definite unele clase utilitare cum ar fi Color, Font, Dimension,
etc. care nu au fost rescrise ˆ Swing.
                               ın
    De asemenea, pachetul java.awt.event r˘mˆne ˆ continuare esential
                                                  a a ın                     ¸
pentru tratarea evenimentelor generate atˆt de componente AWT cˆt ¸i de
                                             a                           a s
cele din Swing. Pe lˆng˘ acesta mai poate fi necesar ¸i javax.swing.event
                     a a                                 s
care descrie tipuri de evenimente specifice unor componente Swing, mecan-
ismul de tratare a lor fiind ˆ a acela¸i ca ˆ AWT.
                             ıns˘      s     ın
    Pozitionarea componentelor este preluat˘ din AWT, fiind ad˘ugate ˆ a
        ¸                                       a                    a      ıns˘
noi clase care descriu gestionari de pozitionare ˆ completarea celor exis-
                                            ¸        ın
tente, cum ar fi BoxLayout ¸i SpringLayout. Difer˘ ˆ a modul de lucru cu
                              s                        a ıns˘
containere, dup˘ cum vom vedea ˆ sectiunea dedicat˘ acestora.
                 a                  ın    ¸               a
    Majoritatea componentelor Swing care permit afi¸area unui text ca parte
                                                        s
a reprezent˘rii lor GUI pot specifica acel text fie ˆ mod normal folosind un
             a                                       ın
anumit font ¸i o anumit˘ culoare ce pot fi setate cu metodele setFont ¸i
               s          a                                                    s
setColor, fie prin intermediul limbajului HTML. Folosirea HTML aduce o
flexibilitatea deosebit˘ ˆ realizarea interfetei grafice, ˆ
                      a ın                    ¸            ıntrucˆt putem aplica
                                                                 a
format˘ri multiple unui text, descompunerea acestuia pe mai multe linii, etc.,
       a
singurul dezavantaj fiind ˆ ıncetinirea etapei de afi¸are a componentelor.
                                                    s

  JButton simplu = new JButton("Text simplu");
  JButton html = new JButton(
    "<html><u>Text</u> <i>formatat</i></html>");

   S˘ descriem o aplictie simpl˘ folosind AWT ¸i apoi Swing, pentru a ne
     a                ¸        a                s
crea o prim˘ impresie asupra diferentelor ¸i asem˘n˘rilor dintre cele dou˘
           a                         ¸     s      a a                    a
modele.

                   Listing 11.1: O aplicatie simpl˘ AWT
                                         ¸        a
import java . awt .*;
import java . awt . event .*;

public class ExempluAWT extends Frame implements
   ActionListener {
  public ExempluAWT ( String titlu ) {
    super ( titlu ) ;
    setLayout ( new FlowLayout () ) ;
    add ( new Label ( " Hello AWT " ) ) ;
    Button b = new Button ( " Close " ) ;
    b . addActionListener ( this ) ;
11.1. INTRODUCERE                                                            303

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




                    Listing 11.2: Aplicatia rescris˘ folosind Swing
                                        ¸          a
import javax . swing .*;
import java . awt .*;
import java . awt . event .*;

public class ExempluSwing extends JFrame implements
   ActionListener {
  public ExempluSwing ( String titlu ) {
    super ( titlu ) ;
    // Metoda setLayout nu se aplica direct ferestrei
    getContentPane () . setLayout ( new FlowLayout () ) ;

      // Componentele au denumiri ce incep cu litera J
      // Textul poate fi si in format HTML
      getContentPane () . add ( new JLabel (
          " < html > <u > Hello </ u > <i > Swing </ i > </ html > " ) ) ;
      JButton b = new JButton ( " Close " ) ;
      b . addActionListener ( this ) ;

      // Metoda add nu se aplica direct ferestrei
      getContentPane () . add ( b ) ;
      pack () ;
      show () ;
    }
    public void actionPerformed ( ActionEvent e ) {
      // Tratarea evenimentelor se face ca in AWT
      System . exit (0) ;
    }
    public static void main ( String args []) {
      new ExempluSwing ( " Hello " ) ;
    }
304                                                 CAPITOLUL 11. SWING

}




11.2       Folosirea ferestrelor
Pentru a fi afi¸ate pe ecran componentele grafice ale unei aplicatii trebuie
                s                                                       ¸
plasate pe o suprafat˘ de afi¸are (container). Fiecare component˘ poate fi
                         ¸a      s                                      a
continut˘ doar ˆ
    ¸     a       ıntr-un singur container, ad˘ugarea ei pe o supraft˘ nou˘
                                                a                         ¸a     a
de afi¸are determinˆnd eliminarea ei de pe vechiul container pe care fusese
      s                a
plasat˘. Intrucˆt containerele pot fi ˆ
      a          a                       ıncapsulate ˆ alte containere, o com-
                                                     ın
ponent˘ va face parte la un moment dat dintr-o ierarhie. R˘d˘cina acestei
        a                                                         a a
ierarhii trebuie s˘ fie un a¸a numit container de nivel ˆ
                  a          s                          ınalt, care este reprezen-
tat de una din clasele JFrame, JDialog sau JApplet. Intrucˆt de appleturi
                                                                   a
ne vom ocupa separat, vom analiza ˆ continuare primele dou˘ clase.
                                       ın                          a
    In general orice aplicatie Java independent˘ bazat˘ pe Swing contine
                               ¸                   a        a                 ¸
cel putin un container de nivel ˆ
       ¸                           ınalt reprezentat de fereastra principal˘ a a
programului, instant˘ a clasei JFrame.
                       ¸a
    Simplificat, un obiect care reprezint˘ o fereastr˘ Swing contine o zon˘
                                            a           a             ¸          a
care este rezervat˘ barei de meniuri ¸i care este situat˘ de obieci ˆ partea sa
                    a                 s                   a           ın
superioar˘ ¸i corpul ferestrei pe care vor fi plasate componentele. Imaginea
           as
de mai jos pune ˆ evident˘ aceast˘ separare, valabil˘ de altfel pentru orice
                   ın         ¸a     a                   a
container de nivel ˆ  ınalt:




   Corpul ferestrei este o instant˘ a clasei Container ce poate fi obtinut˘ cu
                                 ¸a                                 ¸    a
metoda getContentPane. Plasarea ¸i aranjarea componentelor pe suprafata
                                       s                                   ¸
11.2. FOLOSIREA FERESTRELOR                                                  305

ferestrei se va face deci folosind obiectul de tip Container ¸i nu direct fereas-
                                                             s
tra. A¸adar, de¸i este derivat˘ din Frame, clasa JFrame este folosit˘ ˆ
       s          s              a                                     a ıntr-un
mod diferit fat˘ de p˘rintele s˘u:
                ¸a     a          a
  Frame f = new Frame();
  f.setLayout(new FlowLayout());
  f.add(new Button("OK"));

  JFrame jf = new JFrame();
  jf.getContentPane().setLayout(new FlowLayout());
  jf.getContentPane().add(new JButton("OK"));
     Spre deosebire de Frame, un obiect JFrame are un comportament implicit
la ˆ
   ınchiderea ferestrei care const˘ ˆ ascunderea ferestrei atunci cˆnd utiliza-
                                  a ın                             a
torul apas˘ butonul de ˆ
           a              ınchidere. Acest comportament poate fi modificat
prin apelarea metodei setDefaultCloseOperation care prime¸te ca argu-
                                                                  s
ment diverse constante ce se g˘sesc fie ˆ clasa WindowConstants, fie chiar
                                 a       ın
ˆ JFrame.
ın
  jf.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
  jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
  jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   Ad˘ugarea unei bare de meniuri se realizeaz˘ cu metoda setJMenuBar,
      a                                       a
care prime¸te o instant˘ de tip JMenuBar. Crearea meniurilor este similar˘
          s           ¸a                                                 a
cu modelul AWT.

11.2.1     Ferestre interne
Din punctul de vedere al folosirii ferestrelor, aplicatiile pot fi ˆ artite ˆ
                                                      ¸           ımp˘ ¸ ın
dou˘ categorii:
   a
   • SDI (Single Document Interface)
   • MDI (Multiple Document Interface)
Programele din prima categorie gestioneaz˘ la un moment dat o singur˘
                                               a                              a
fereastr˘ ˆ care se g˘sesc componentele cu care interactioneaz˘ utilizatorul.
        a ın          a                                   ¸       a
In a doua categorie, fereastra principal˘ a aplicatiei ˆ
                                          a          ¸ ınglobeaz˘ la rˆndul ei
                                                                  a    a
alte ferestre, uzual cu functionalit˘¸i similare, ce permit lucrul concurent pe
                            ¸       at
mai multe planuri.
306                                                        CAPITOLUL 11. SWING

    In Swing, clasa JInternalFrame pune la dispozitie o modalitate de a
                                                          ¸
crea ferestre ˆ cadrul altor ferestre. Ferestrele interne au aproximativ aceea¸i
              ın                                                               s
ˆ a¸i¸are ¸i functionalitate cu ferestrele de tip JFrame, singura diferent˘ fiind
ınf˘t s    s      ¸                                                       ¸a
modul de gestionare a acestora.
    Uzual, obiectele de tip JInternalFrame vor fi plasate pe un container
de tip DesktopPane, care va fi apoi plasat pe o fereastr˘ de tip JFrame.
                                                               a
Folosirea clasei DesktopPane este necesar˘ deoarece aceasta ”¸tie” cum s˘
                                              a                    s           a
gestioneze ferestrele interne, avˆnd ˆ vedere c˘ acestea se pot suprapune ¸i
                                  a ın             a                           s
la un moment dat doar una singur˘ este activ˘.
                                     a            a
    Exemplul urm˘tor pune ˆ evident˘ modelul general de creare ¸i afi¸are
                    a          ın       ¸a                              s    s
a ferestrelor interne:


                     Listing 11.3: Folosirea ferestrelor interne
import javax . swing .*;
import java . awt .*;

class FereastraPrinci pal a extends JFrame {
    public Fereastra Pr i nc i p al a ( String titlu ) {
      super ( titlu ) ;
      setSize (300 , 200) ;
      s e tD e f au l t C lo s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ;

        FereastraInterna fin1 = new FereastraInterna () ;
        fin1 . setVisible ( true ) ;
        FereastraInterna fin2 = new FereastraInterna () ;
        fin2 . setVisible ( true ) ;

         JDesktopPane desktop = new JDesktopPane () ;
         desktop . add ( fin1 ) ;
         desktop . add ( fin2 ) ;
         setContentPane ( desktop ) ;

        fin2 . moveToFront () ;
    }
}

class FereastraInterna extends JInternalFrame {
  static int n = 0; // nr . de ferestre interne
  static final int x = 30 , y = 30;

    public FereastraInterna () {
      super ( " Document # " + (++ n ) ,
11.3. CLASA JCOMPONENT                                                   307

           true , // resizable
           true , // closable
           true , // maximizable
           true ) ; // iconifiable
     setLocation ( x *n , y * n ) ;
     setSize ( new Dimension (200 , 100) ) ;
  }
}
public class TestInternalFrame {
  public static void main ( String args []) {
    new FereastraPrincipala ( " Test ferestre interne " ) . show () ;
  }
}


   Ferestrele create de acest program vor ar˘ta ca ˆ figura de mai jos:
                                            a      ın




11.3      Clasa JComponent
JComponent este superclasa tuturor componentelor Swing, mai putin a celor
                                                                  ¸
care descriu containere de nivel ˆınalt JFrame, JDialog, JApplet. Deoarece
JComponent extinde clasa Container, deci ¸i Component, ea mo¸tene¸te functionalitatea
                                           s                 s      s      ¸
general˘ a containerelor ¸i componentelor AWT, furnizˆnd bineˆ ¸eles ¸i o se-
        a                 s                           a       ınt     s
rie ˆ
    ıntreag˘ de noi facilit˘¸i.
           a               at
    Dintre nout˘¸ile oferite de JComponent amintim:
                at

   • ToolTips
     Folosind metoda setToolTip poate fi ata¸at unei componente un text
                                              s
     cu explicatii legate de componenta respectiv˘. Cˆnd utilizatorul trece
               ¸                                 a a
308                                               CAPITOLUL 11. SWING

      cu mouse-ul deasupra componentei va fi afi¸at, pentru o perioad˘ de
                                              s                    a
      timp, textul ajut˘tor specificat.
                       a

   • Chenare
     Orice component˘ Swing poate avea unul sau mai multe chenare. Speci-
                     a
     ficarea unui chenar se realizeaz˘ cu metoda setBorder.
                                    a

   • Suport pentru plasare ¸i dimensionare
                                 s
     Folosind metodele setPreferredSize, setMinimumSize, setMaximumSize,
     setAlignmentX, setAlignmentY pot fi controlati parametrii folositi
                                                         ¸                ¸
     de gestionarii de pozitionare pentru plasarea ¸i dimensionarea automat˘
                           ¸                       s                       a
     a componentelor ˆ cadrul unui container.
                        ın

   • Controlul opacit˘¸ii
                        at
     Folosind metoda setOpaque vom specifica dac˘ o component˘ trebuie
                                                       a             a
     sau nu s˘ deseneze toti pixelii din interiorul s˘u. Implicit, valoarea
              a              ¸                          a
     propriet˘¸ii de opacitate este false, ceea ce ˆ
             at                                       ınseamn˘ c˘ este posibil
                                                             a a
     s˘ nu fie desenati unii sau chiar toti pixelii, permitˆnd pixelilor de sub
      a              ¸                   ¸                ¸a
     component˘ s˘ r˘mˆn˘ vizibili (componenta nu este opac˘). Valoarea
                a a a a a                                        a
     propriet˘¸ii pentru clasele derivate din JComponent depinde ˆ general
             at                                                     ın
     de Look-and-Feel-ul folosit.

   • Asocierea de actiuni tastelor
                       ¸
     Pentru componentele Swing exist˘ posibilitatea de specifica anumite
                                       a
     actiuni care s˘ se execute atunci cˆnd utilizatorul apas˘ o anumit˘
       ¸           a                     a                     a          a
     combinatie de taste ¸i componenta respectiv˘ este activ˘ (are focus-
             ¸            s                        a           a
     ul). Aceast˘ facilitate simplific˘ varianta initial˘ de lucru, ¸i anume
                 a                   a             ¸ a             s
     tratarea evenimentelor de tip KeyEvent printr-un obiect KeyListener.

   • Double-Buffering
     Tehnica de double-buffering, care implic˘ desenarea componentei ˆ
                                                a                         ın
     memorie ¸i apoi transferul ˆ
              s                 ıntregului desen pe ecran, este implementat˘
                                                                           a
     automat de componentele Swing, spre deosebire de cele AWT unde
     trebuia realizat˘ manual dac˘ era cazul.
                     a             a

   Exemplul urm˘tor ilustreaz˘ modul de folosire a cˆtorva dintre facilit˘¸ile
                  a          a                      a                    at
amintite mai sus:

            Listing 11.4: Facilit˘¸i oferite de clasa JComponent
                                 at
11.3. CLASA JCOMPONENT                                                                309

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

class Fereastra extends JFrame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    getContentPane () . setLayout ( new FlowLayout () ) ;
    s e tD e f au l t C lo s e Op e r at i o n ( JFrame . EXIT_ON_CLOSE ) ;

     // Folosirea chenarelor
     Border lowered , raised ;
     TitledBorder title ;

     lowered = BorderFactory . c r e a t e L o w e r e d B e v e l B o r d e r () ;
     raised = BorderFactory . c r e a t e R a i s e d B e v e l B o r d e r () ;
     title = BorderFactory . c re at eT it led Bo rd er ( " Borders " ) ;

     final JPanel panel = new JPanel () ;
     panel . setPreferredSize ( new Dimension (400 ,200) ) ;
     panel . setBackground ( Color . blue ) ;
     panel . setBorder ( title ) ;
     getContentPane () . add ( panel ) ;

     JLabel label1 = new JLabel ( " Lowered " ) ;
     label1 . setBorder ( lowered ) ;
     panel . add ( label1 ) ;

     JLabel label2 = new JLabel ( " Raised " ) ;
     label2 . setBorder ( raised ) ;
     panel . add ( label2 ) ;

     // Controlul opacitatii
     JButton btn1 = new JButton ( " Opaque " ) ;
     btn1 . setOpaque ( true ) ; // implicit
     panel . add ( btn1 ) ;

     JButton btn2 = new JButton ( " Transparent " ) ;
     btn2 . setOpaque ( false ) ;
     panel . add ( btn2 ) ;

     // ToolTips
     label1 . setToolTipText ( " Eticheta coborata " ) ;
     label2 . setToolTipText ( " Eticheta ridicata " ) ;
310                                                  CAPITOLUL 11. SWING

        btn1 . setToolTipText ( " Buton opac " ) ;
        // Textul poate fi HTML
        btn2 . setToolTipText ( " < html > <b > Apasati < font color = red > F2
           </ font > " +
             " cand butonul are <u > focusul </ u > " ) ;

        // Asocierea unor actiuni ( KeyBindings )
        /* Apasarea tastei F2 cand focusul este pe butonul al
             doilea
             va determina schimbarea culorii panelului */
        btn2 . getInputMap () . put ( KeyStroke . getKeyStroke ( " F2 " ) ,
                                       " schimbaCuloare " ) ;
        btn2 . getActionMap () . put ( " schimbaCuloare " , new
             AbstractAction () {
           private Color color = Color . red ;
           public void actionPerformed ( ActionEvent e ) {
              panel . setBackground ( color ) ;
              color = ( color == Color . red ? Color . blue : Color . red )
                 ;
           }
        }) ;

        pack () ;
    }
}

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




11.4         Arhitectura modelului Swing

11.5         Folosirea modelelor
Modelul Swing este bazat pe o arhitectur˘ asem˘n˘toare cu MVC (model-
                                        a     a a
view-controller). Arhitectura MVC specific˘ descompunerea unei aplicatii
                                          a                         ¸
vizuale ˆ trei p˘rti separate:
        ın      a¸

    • Modelul - care va reprezenta datele aplicatiei.
                                                ¸
11.5. FOLOSIREA MODELELOR                                                  311

   • Prezentarea - modul de reprezentare vizual˘ a datelor.
                                               a

   • Controlul - transformarea actiunilor utilizatorului asupra componen-
                                 ¸
     telor vizuale ˆ evenimente care s˘ actualizeze automat modelul aces-
                   ın                 a
     tora (datele).

    Din motive practice, ˆ Swing p˘rtile de prezentare ¸i control au fost cu-
                          ın          a¸                    s
plate deoarece exista o leg˘tur˘ prea strˆns˘ ˆ
                            a a            a a ıntre ele pentru a fi concepute
ca entit˘¸i separate. A¸adar, arhitectura Swing este de fapt o arhitectur˘
        at               s                                                    a
cu model separabil, ˆ care datele componentelor (modelul) sunt separate de
                    ın
reprezentarea lor vizual˘. Aceast˘ abordare este logic˘ ¸i din perspectiva
                         a          a                       a s
faptului c˘, ˆ general, modul de concepere a unei aplicatii trebuie s˘ fie ori-
           a ın                                              ¸        a
entat asupra reprezent˘rii ¸i manipul˘rii informatiilor ¸i nu asupra interfetei
                        a s            a          ¸       s                 ¸
grafice cu utilizatorul.
    Pentru a realiza separarea modelului de prezentare, fiec˘rui obiect core-
                                                                a
spunz˘tor unei clase ce descrie o component˘ Swing ˆ este asociat un obiect
      a                                      a         ıi
care gestioneaz˘ datele sale ¸i care implementeaz˘ o interfat˘ care reprezint˘
                a            s                    a            ¸a             a
modelul componentei respective. Dup˘ cum se observ˘ din tabelul de mai
                                         a                 a
jos, componente cu reprezent˘ri diferite pot avea acela¸i tip de model, dar
                               a                            s
exist˘ ¸i componente care au asociate mai multe modele:
     as
  Model              Component˘ a
  ButtonModel        JButton, JToggleButton, JCheckBox,
                     JRadioButton, JMenu, JMenuItem,
                     JCheckBoxMenuItem, JRadioButtomMenuItem
  JComboBox          ComboBoxModel
  BoundedRangeModel  JProgressBar, JScrollBarm, JSlider
  JTabbedPane        SingleSelectionModel
  ListModel          JList
  ListSelectionModel JList
  JTable             TableModel
  JTable             TableColumnModel
  JTree              TreeModel
  JTree              TreeSelectionModel
  Document           JEditorPane, JTextPane, JTextArea,
                     JTextField, JPasswordField

   Fiecare component˘ are un model initial implicit, ˆ a are posibilitatea
                       a                 ¸           ıns˘
de a-l ˆ
       ınlocui cu unul nou atunci cˆnd este cazul. Metodele care acceseaz˘
                                   a                                     a
312                                                        CAPITOLUL 11. SWING

modelul unui obiect sunt: setModel, respectiv getModel, cu argumente
specifice fiec˘rei componente ˆ parte. Crearea unei clase care s˘ reprezinte
             a                 ın                                a
un model se va face extinzˆnd interfata corespunz˘toare ¸i implementˆnd
                             a           ¸           a      s            a
metodele definite de aceasta sau extinzˆnd clasa implicit˘ oferit˘ de API-ul
                                         a                a      a
Swing ¸i supradefinind metodele care ne intereseaz˘. Pentru modelele mai
       s                                             a
complexe, cum ar fi cele asociate claselor JTable, JTree sau JList exist˘    a
clase abstracte care implementeaz˘ interfata ce descrie modelul respectiv De
                                   a         ¸
exemplu, interfata model a clasei JList este ListModel care este imple-
                  ¸
mentat˘ de clasele DefaultListModel ¸i AbstractListModel. In functie
       a                                   s                              ¸
de necesit˘¸i, oricare din aceste clase poate fi extins˘ pentru a crea un nou
          at                                          a
model.


      Listing 11.5: Folosirea mai multor modele pentru o componenta
import   javax . swing .*;
import   javax . swing . border .*;
import   java . awt .*;
import   java . awt . event .*;

class Fereastra extends JFrame implements ActionListener {
  String data1 [] = { " rosu " , " galben " , " albastru " };
  String data2 [] = { " red " , " yellow " , " blue " };
  int tipModel = 1;
  JList lst ;
  ListModel model1 , model2 ;

  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    s e tD e f au l t C lo s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ;

      // Lista initiala nu are nici un model
      lst = new JList () ;
      getContentPane () . add ( lst , BorderLayout . CENTER ) ;

      // La apasara butonului schimbam modelul
      JButton btn = new JButton ( " Schimba modelul " ) ;
      getContentPane () . add ( btn , BorderLayout . SOUTH ) ;
      btn . addActionListene r ( this ) ;

      // Cream obiectele corespunzatoare celor doua modele
      model1 = new Model1 () ;
      model2 = new Model2 () ;
      lst . setModel ( model1 ) ;
11.5. FOLOSIREA MODELELOR                                                313


        pack () ;
    }

    public void actionPerformed ( ActionEvent e ) {
      if ( tipModel == 1) {
        lst . setModel ( model2 ) ;
        tipModel = 2;
      }
      else {
        lst . setModel ( model1 ) ;
        tipModel = 1;
      }
    }

    // Clasele corespunzatoare celor doua modele
    class Model1 extends Abst ractLis tModel {
      public int getSize () {
          return data1 . length ;
      }
      public Object getElementAt ( int index ) {
        return data1 [ index ];
      }
    }

    class Model2 extends Abst ractLis tModel {
      public int getSize () {
          return data2 . length ;
      }
      public Object getElementAt ( int index ) {
        return data2 [ index ];
      }
    }

}


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

   Multe componente Swing furnizeaz˘ metode care s˘ obtin˘ starea obiec-
                                        a               a ¸ a
tului f˘r˘ a mai fi nevoie s˘ obtinem instanta modelului ¸i s˘ apel˘m metodele
       aa                  a ¸             ¸            s a       a
314                                                 CAPITOLUL 11. SWING

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

11.5.1      Tratarea evenimentelor
Modelele componentelor trebuie s˘ notifice aparitia unor schimb˘ri ale datelor
                                  a              ¸              a
gestionate astfel ˆ at s˘ poat˘ fi reactualizat˘ prezentarea lor sau s˘ fie ex-
                  ıncˆ a       a               a                     a
ecutat un anumti cod ˆ cadrul unui obiect de tip listener. In Swing, aceast˘
                        ın                                                  a
notificare este realizat˘ ˆ dou˘ moduri:
                        a ın   a
   1. Informativ (lightweight) - Modelele trimit un eveniment prin care
sunt informati ascult˘torii c˘ a survenit o anumit˘ schimbare a datelor, f˘r˘
              ¸       a      a                     a                      aa
a include ˆ eveniment detalii legate de schimbarea survenit˘. Obiectele
           ın                                                   a
de tip listener vor trebui s˘ apeleze metode specifice componentelor pen-
                             a
tru a afla ce anume s-a schimbat. Acest lucru se realizeaz˘ prin intefata
                                                              a            ¸
ChangeListener iar evenimentele sunt de tip ChangeEvent, modelele
care suport˘ aceast˘ abordare fiind BoundedRangeModel, ButtonModel ¸i
            a        a                                                      s
SingleSelectionModel.
        Model                         Listener          Tip Eveniment
        BoundedRangeModel             ChangeListener    ChangeEvent
        ButtonModel                   ChangeListener    ChangeEvent
        SingleSelectionModelModel     ChangeListener    ChangeEvent

   Interfata ChangeListener are o singur˘ metod˘:
          ¸                             a      a
               public void stateChanged(ChangeEvent e),
singura informatie continut˘ ˆ eveniment fiind componenta surs˘.
                 ¸       ¸    a ın                                     a
    Inregistrarea ¸i eliminarea obiectelor de tip listener se realizeaz˘ cu metodele
                  s                                                    a
addChangeListener, respectiv removeChangeListener.

JSlider slider = new JSlider();
BoundedRangeModel model = slider.getModel();
model.addChangeListener(new ChangeListener() {
   public void stateChanged(ChangeEvent e) {
      // Sursa este de tip BoundedRangeModel
      BoundedRangeModel m = (BoundedRangeModel)e.getSource();
      // Trebuie sa interogam sursa asupra schimbarii
11.5. FOLOSIREA MODELELOR                                                315

       System.out.println("Schimbare model: " + m.getValue());
   }
});

    Pentru u¸urinta program˘rii, pentru a nu lucra direct cu instanta modelu-
             s    ¸          a                                      ¸
lui, unele clase permit ˆ
                        ınregistrarea ascult˘torilor direct pentru componenta
                                            a
ˆ sine, singura diferent˘ fat˘ de varianta anterioar˘ constˆnd ˆ faptul c˘
ın                       ¸a ¸a                         a       a ın         a
sursa evenimentului este acum de tipul componentei ¸i nu de tipul modelului.
                                                       s
Secventa de cod de mai sus poate fi rescris˘ astfel:
       ¸                                     a

JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener() {
   public void stateChanged(ChangeEvent e) {
      // Sursa este de tip JSlider
      JSlider s = (JSlider)e.getSource();
      System.out.println("Valoare noua: " + s.getValue());
   }
});

    2. Consistent(statefull) - Modele pun la dispozitie interfete special-
                                                        ¸          ¸
izate ¸i tipuri de evenimente specifice ce includ toate informatiile legate de
      s                                                       ¸
schimbarea datelor.
 Model                Listener                     Tip Eveniment
 ListModel            ListDataListener             ListDataEvent
 ListSelectionModel   ListSelectionListener        ListSelectionEvent
 ComboBoxModel        ListDataListener             ListDataEvent
 TreeModel            TreeModelListener            TreeModelEvent
 TreeSelectionModel   TreeSelectionListener        TreeSelectionEvent
 TableModel           TableModelListener           TableModelEvent
 TableColumnModel     TableColumnModelListener     TableColumnModelEvent
 Document             DocumentListener             DocumentEvent
 Document             UndoableEditListener         UndoableEditEvent

   Folosirea acestor interfete nu difer˘ cu nimic de cazul general:
                            ¸          a

String culori[] = {"rosu", "galben", "albastru");
JList list = new JList(culori);
ListSelectionModel sModel = list.getSelectionModel();
sModel.addListSelectionListener(
316                                              CAPITOLUL 11. SWING

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


11.6      Folosirea componentelor
Datorit˘ complexit˘¸ii modelului Swing, ˆ aceast˘ secttiune nu vom ˆ
        a          at                    ın       a     ¸             ıncerca
o abordare exhaustiv˘ a modului de utilizare a tuturor componentelor, ci vom
                     a
pune ˆ evident˘ doar aspectele specifice acestui model, subliniind diferentele
     ın        ¸a                                                        ¸
¸i ˆ
s ımbun˘t˘¸irile fat˘ AWT.
          a at     ¸a

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

11.6.2     Componente pentru editare de text
Componentele Swing pentru afi¸area ¸i editarea textelor sunt grupate ˆ
                                  s    s                            ıntr-o
ierarhie ce are ca r˘d˘cin˘ clasa JTextComponent din pachetul javax.swing.text.
                    a a a
11.6. FOLOSIREA COMPONENTELOR                                            317




    Dup˘ cum se observ˘ din imaginea de mai sus, clasele pot ˆ artite ˆ
        a                a                                   ımp˘ ¸ ın
trei categorii, corespunz˘toare tipului textului editat:
                         a

   • Text simplu pe o singur˘ linie
                            a

       – JTextField - Permite editarea unui text simplu, pe o singur˘
                                                                    a
         linie.
       – JPasswordField - Permite editarea de parole. Textul acestora
         va fi ascuns, ˆ locul caracterelor introduse fiind afi¸at un caracter
                      ın                                    s
         simbolic, cum ar fi ’*’.
       – JFormattedTextField - Permite introducerea unui text care s˘    a
         respecte un anumit format, fiind foarte util˘ pentru citirea de
                                                       a
         numere, date calendaristice, etc. Este folosit˘ ˆ
                                                       a ımpreun˘ cu clase
                                                                 a
         utilitare pentru formatarea textelor, cum ar fi NumberFormatter,
         DateFormatter, MaskFormatter, etc. Valoarea continut˘ de o
                                                               ¸   a
         astfel de component˘ va fi obtinut˘/setat˘ cu metodele getValue,
                             a        ¸    a      a
         respectiv setValue ¸i nu cu cele uzuale getText, setText.
                              s

   • Text simplu pe mai multe linii

       – JTextArea - Permite editarea unui text simplu, pe mai multe
         linii. Orice atribut legat de stil, cum ar fi culoarea sau fontul, se
         aplic˘ ˆ
               a ıntregului text ¸i nu poate fi specificat doar unei anumite
                                 s
         portiuni. Uzual, o component˘ de acest tip va fi inclus˘ ˆ
             ¸                            a                        a ıntr-un
         container JScrollPane, pentru a permite navigarea pe vertical˘     a
318                                               CAPITOLUL 11. SWING

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

   • Text cu stil ˆ
                  ımbog˘¸it pe mai multe linii
                       at

        – JEditorPane - Permite afi¸area ¸i editarea de texte scrise cu stil-
                                       s     s
          uri multiple ¸i care pot include imagini sau chiar diverse alet com-
                       s
          ponente. Implicit, urm˘toarele tipuri de texte sunt recunoscute:
                                   a
          text/plain, text/html ¸i text/rtf. Una din utiliz˘rile cele mai
                                     s                          a
          simple ale acestei clase este setarea documentului ce va fi afi¸at  s
          cu metoda setPage, ce prime¸te ca argument un URL care poate
                                         s
          referi un fi¸ier text, HTML sau RTF.
                     s
        – JTextPane - Aceast˘ clas˘ extinde JEditorPane, oferind diverse
                                a    a
          facilit˘¸i suplimentare pentru lucrul cu stiluri ¸i paragrafe.
                 at                                        s

    Clasa JTextComponent ˆ   ıncearc˘ s˘ p˘streze cˆt mai multe similitudini cu
                                    a a a          a
clasa TextComponent din AWT, ˆ a exist˘ diferente notabile ˆ
                                  ıns˘     a         ¸         ıntre cele dou˘,
                                                                              a
componenta Swing avˆnd caracteristici mult mai complexe cum ar fi suport
                       a
pentru operatii de undo ¸i redo, tratarea evenimentelor generate de cursor
              ¸            s
(caret), etc. Orice obiect derivat din JTextComponent este format din:

   • Un model, referit sub denumirea de document, care gestioneaz˘ starea
                                                                   a
     componentei. O referint˘ la model poate fi obtinut˘ cu metoda getDocument,
                           ¸a                    ¸    a
     ce returneaz˘ un obiect de tip Document.
                 a

   • O reprezentare, care este responsabil˘ cu afi¸area textului.
                                          a      s

   • Un ’controller’, cunoscut sub numele de editor kit care permite scrierea
     ¸i citirea textului ¸i care permite definirea de actiuni necesare edit˘rii.
     s                   s                             ¸                  a

   Exist˘ diferent fat˘ de AWT ¸i la nivelul trat˘rii evenimentelor generate
        a        ¸ ¸a          s                 a
de componentele pentru editarea de texte. Dintre evenimentele ce pot fi
generate amintim:

   • ActionEvent - Componentele derivate din JTextField vor genera un
     eveniment de acest tip la ap˘sarea tastei Enter ˆ c˘suta de editare a
                                   a                 ın a ¸
     textului. Interfata care trebuie implementat˘ este ActionListener.
                      ¸                          a
11.6. FOLOSIREA COMPONENTELOR                                            319

   • CaretEvent - Este evenimentul generat la deplasarea cursorului ce ges-
     tioneaz˘ pozitia curent˘ ˆ text. Interfata corespunz˘toare CaretLis-
            a     ¸         a ın             ¸           a
     tener contine o singur˘ metod˘: caretUpdate ce va fi apelat˘ ori de
                ¸           a       a                             a
     cˆte ori apare o schimbare.
      a


   • DocumentEvent - Evenimentele de acest tip sunt generate la orice
     schimbare a textului, sursa lor fiind documentul (modelul) componen-
     tei ¸i nu componenta ˆ sine. Interfata corespunz˘toare este Docu-
         s                  ın              ¸          a
     mentListener, ce contine metodele:
                            ¸


         – insertUpdate - apelat˘ la ad˘ugarea de noi caractere;
                                a      a

         – removeUpdate - apelat˘ dup˘ o operatiune de ¸tergere;
                                a    a        ¸        s

         – changedUpdate - apelat˘ la schimbarea unor atribute legate de
                                 a
           stilul textului.


   • PropertyChangeEvent - Este un eveniment comun tuturor com-
     ponentelor de tip JavaBean, fiind generat la orice schimbare a unei
     propriet˘¸i a componentei. Interfata corespunz˘toare este Property-
             at                        ¸           a
     ChangeListener, ce contine metoda propertyChange.
                             ¸



11.6.3     Componente pentru selectarea unor elemente

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


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




  Initializarea unei liste se realizeaz˘ ˆ mai multe modalit˘¸i:
     ¸                                 a ın                 at

  • Folosind unul din constructorii care primesc ca argument un vector de
    elemente.

      Object elemente[] = {"Unu", "Doi", new Integer(3), new Double(4)};
      JList lista = new JList(elemente);

  • Folosind constructorul f˘r˘ argumente ¸i ad˘ugˆnd apoi elemente mod-
                            aa            s    a a
    elului implicit listei:

      DefaultListModel model = new DefaultListModel();
      model.addElement("Unu");
      model.addElement("Doi");
      model.addElement(new Integer(3));
      model.addElement(new Double(4));
      JList lista = new JList(model);

  • Folosind un model propriu, responsabil cu furnizarea elementelor lis-
    tei. Acesta este un obiect dintr-o clas˘ ce trebuie s˘ implementeze
                                            a                 a
    interfata ListModel, uzual fiind folosit˘ extinderea clasei predefinite
           ¸                                a
    AbstractListModel ¸i supradefinirea metodelor: getElementAt care
                           s
    furnizeaz˘ elementul de pe o anumit˘ positie din list˘, respectiv getSize
              a                        a     ¸           a
    care trebuie s˘ returneze num˘rul total de elemente din list˘. Evident,
                   a              a                               a
    aceast˘ variant˘ este mai complex˘, oferind flexibilitate sporit˘ ˆ lu-
           a         a                a                               a ın
    crul cu liste.

      ModelLista model = new ModelLista();
      JList lista = new JList(model);
11.6. FOLOSIREA COMPONENTELOR                                               321

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




    Gestiunea articolelor selectate dintr-o list˘ se realizeaz˘ prin intermediul
                                                a             a
unui model, acesta fiind un obiect de tip ListSelectionModel. Obiectele de
tip JList genereaz˘ evenimente de tip ListSelectionEvent, interfata core-
                   a                                                     ¸
spunz˘toare fiind ListSelectionListener ce contine metoda valueChanged
      a                                            ¸
apelat˘ ori de cˆte ori va fi schimbat˘ selectia elementelor din list˘.
      a         a                      a      ¸                        a

class Test implements ListSelectionListener {
  ...
  public Test() {
    ...
   // Stabilim modul de selectie
   list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        /* sau SINGLE_INTERVAL_SELECTION
               MULTIPLE_INTERVAL_SELECTION
        */

   // Adaugam un ascultator
   ListSelectionModel model = list.getSelectionModel();
   model.addListSelectionListener(this);
    ...
  }

  public void valueChanged(ListSelectionEvent e) {
    if (e.getValueIsAdjusting()) return;
    int index = list.getSelectedIndex();
322                                                CAPITOLUL 11. SWING

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


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


    Clasa JComboBox
Clasa JComboBox este similar˘ cu JList, cu deosebirea c˘ permite doar se-
                               a                          a
lectarea unui singur articol, acesta fiind ¸i singurul permanent vizibil. Lista
                                          s
11.6. FOLOSIREA COMPONENTELOR                                              323

celorlalte elemente este afi¸at˘ doar la ap˘sarea unui buton marcat cu o
                            s a            a
s˘geat˘, ce face parte integrant˘ din component˘.
 a     a                        a              a




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


    Clasa JSpinner
Clasa JSpinner ofer˘ posibilitatea de a selecta o anumit˘ valoare (element)
                     a                                    a
dintr-un domeniu prestabilit, lista elementelor nefiind ˆ a vizibil˘. Este
                                                          ıns˘        a
folosit atunci cˆnd domeniul din care poate fi f˘cut˘ selectia este foarte mare
                a                              a a        ¸
sau chiar nem˘rginit; de exemplu: numere intregi intre 1950 si 2050. Com-
               a
ponenta contine dou˘ butoane cu care poate fi selectat urm˘torul, respectiv
             ¸       a                                       a
predecesorul element din domeniu.




   JSpiner se bazeaz˘ exclusiv pe folosirea unui model. Acesta este un
                      a
obiect de tip SpinnerModel, existˆnd o serie de clase predefinite ce imple-
                                    a
menteaz˘ aceast˘ interfat˘ cum ar fi SpinnerListModel, SpinnerNumberModel
        a       a       ¸a
sau SpinnerDateModel ce pot fi utilizate.
324                                                CAPITOLUL 11. SWING

    Componentele de acest tip permit ¸i specificarea unui anumit tip de
                                         s
editor pentru valorile elementelor sale. Acesta este instalat automat pen-
tru fiecare din modelele standard amintite mai sus, fiind reprezentat de
una din clasele JSpinner.ListEditor, JSpinner.NumberEditor, respectiv
JSpinner.DateEditor, toate derivate din JSpinner.DefaultEditor. Fiecare
din editoarele amintite permite diverse format˘ri specifice.
                                                a
    Evenimentele generate de obiectele de tip JSpinner sunt de tip ChangeEvent,
generate la schimbarea st˘rii componentei.
                          a

11.6.4     Tabele
Clasa JTable permite crearea de componente care s˘ afi¸eze o serie de ele-
                                                         a s
mente ˆ ıntr-un format tabelar, articolele fiind dispuse pe linii ¸i coloane. Un
                                                                  s
tabel poate fi folosit doar pentru afi¸area formatat˘ a unor date, dar este
                                        s               a
posibil˘ ¸i editarea informatiei din celulele sale. De asemenea, liniile tabelu-
       as                   ¸
lui pot fi marcate ca selectate, tipul selectiei fiind simplu sau compus, tabelele
                                           ¸
extinzˆnd astfel functionalitatea listelor.
       a              ¸




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

editabile trebuie s˘ existe un editor potrivit pentru tipul elementului din
                    a
celula respectiv˘. Din motive de eficient˘, implementarea acestei clase este
                 a                        ¸a
orientat˘ la nivel de coloan˘, ceea ce ˆ
         a                    a         ınseamn˘ c˘ articole de pe o coloan˘
                                                  a a                         a
vor fi reprezentate la fel ¸i vor avea acela¸i tip de editor.
                          s                s
    A doua variant˘ de creare a unui tabel este prin implementarea modelu-
                    a
lui acestuia ˆ
             ıntr-o clas˘ separat˘ ¸i folosirea constructorului corespunz˘tor.
                        a         as                                      a
Interfata care descrie modelul clasei JTable este TableModel ¸i contine
       ¸                                                             s     ¸
metodele care vor fi interogate pentru obtinerea informatiei din tabel. Uzual,
                                          ¸               ¸
crearea unui model se face prin extinderea clasei predefinite AbstractTable-
Model, care implementeaz˘ deja TableModel. Tot ceea ce trebuie s˘ facem
                             a                                         a
este s˘ supradefinim metodele care ne intereseaz˘, cele mai utilizate fiind
      a                                              a
(primele trei trebuie obligatoriu supradefinite, ele fiind declarate abstracte ˆ
                                                                             ın
clasa de baz˘):
             a

   • getRowCount - returneaz˘ num˘rul de linii ale tabelului;
                            a    a

   • getColumnCount - returneaz˘ num˘rul de coloane ale tabelului;
                               a    a

   • getValueAt - returneaz˘ elementul de la o anumit˘ linie ¸i coloan˘;
                           a                         a       s        a

   • getColumnName - returneaz˘ denumirea fiec˘rei coloane;
                              a              a

   • isCellEditable - specific˘ dac˘ o anumit˘ celul˘ este editabil˘.
                             a    a         a      a              a

Modelul mai contine ¸i metoda setValueAt care poate fi folosit˘ pentru
                  ¸    s                                     a
setarea explicit˘ a valorii unei celule.
                a

  ModelTabel model = new ModelTabel();
  JTable tabel = new JTable(model);
  ...
  class ModelTabel extends AbstractTableModel {
    String[] coloane = {"Nume", "Varsta", "Student"};
    Object[][] elemente = {
      {"Ionescu", new Integer(20), Boolean.TRUE},
      {"Popescu", new Integer(80), Boolean.FALSE}};

     public int getColumnCount() {
       return coloane.length;
     }
     public int getRowCount() {
326                                             CAPITOLUL 11. SWING

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



    Orice schimbare a datelor tabelului va genera un eveniment de tip Table-
ModelEvent. Pentru a trata aceste evenimente va trebui s˘ implement˘m
                                                             a           a
interfata TableModelListener ce contine metoda tableChanged. Inreg-
       ¸                                 ¸
istrarea unui listener va fi f˘cut˘ pentru modelul tabelului:
                             a a


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

   Tabele ofer˘ posibilitatea de a selecta una sau mai multe linii, nu neap˘rat
               a                                                             a
consecutive, gestiunea liniilor selectate fiind realizat˘ prin intermediul unui
                                                       a
model. Acesta este o instant˘ ce implementeaz˘, ˆ
                             ¸a                 a ıntocmai ca la liste, interfata
                                                                               ¸
ListSelectionModel. Tratarea evenimentelor generate de schimbarea selectiei     ¸
ˆ tabel se realizeaz˘ prin ˆ
ın                  a      ınregistrarea unui ascult˘tor de tip ListSelection-
                                                    a
Listener:
class Test implements ListSelectionListener {
  ...
  public Test() {
    ...
    // Stabilim modul de selectie
   tabel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

      // Adaugam un ascultator
     ListSelectionModel model = tabel.getSelectionModel();
     model.addListSelectionListener(this);
      ...
    }

    public void valueChanged(ListSelectionEvent e) {
      if (e.getValueIsAdjusting()) return;
      ListSelectionModel model =
        (ListSelectionModel)e.getSource();
      if (model.isSelectionEmpty()) {
        // Nu este nici o linie selectata
        ...
      } else {
        int index = model.getMinSelectionIndex();
        // Linia cu numarul index este prima selectata
        ...
      }
    }
}



    Dup˘ cum am spus, celule unei coloane vor fi reprezentare la fel, fiecare
       a
coloan˘ avˆnd asociat un obiect renderer responsabil cu crearea componen-
      a a
328                                                 CAPITOLUL 11. SWING

tei ce descrie celulele sale. Un astfel de obiect implementeaz˘ interfata
                                                                     a        ¸
TableCellRenderer, care are o singur˘ metod˘ getTableCellRender-
                                            a        a
erComponent, aceasta fiind responsabil˘ cu crearea componentelor ce vor
                                            a
fi afi¸ate ˆ celulele unei coloane. Implicit, exist˘ o serie de tipuri de date
     s     ın                                       a
cu reprezent˘ri specifice, cum ar fi: Boolean, Number, Double, Float,
              a
Date, ImageIcon, Icon, restul tipurilor avˆnd o reprezentare standard ce
                                               a
const˘ ˆ
      a ıntr-o etichet˘ cu reprezentarea obiectului ca ¸ir de caractere. Specifi-
                      a                                s
carea unui renderer propriu se realizeaz˘ cu metoda setDefaultRenderer,
                                          a
ce asociaz˘ un anumit tip de date cu un obiect de tip TableRenderer.
            a

public class MyRenderer extends JLabel
    implements TableCellRenderer {
  public Component getTableCellRendererComponent(...) {
    ...
    return this;
  }
}



    O situatie similar˘ o reg˘sim la nivelul editorului asociat celulelor dintr-o
           ¸          a      a
anumit˘ coloan˘. Acesta este un obiect ce implementeaz˘ interfata Tree-
        a        a                                            a        ¸
CellEditor, ce extinde interfata CellEditor care generalizeaz˘ conceptul
                                 ¸                                  a
de celul˘ editabil˘ pe care ˆ vom mai reg˘si la arbori. Implicit, exist˘ o
         a         a          ıl              a                              a
serie de editoare standard pentru tipurile de date mentionate anterior, dar
                                                           ¸
este posibil˘ specificarea unui editor propriu cu metoda setDefaultEditor.
            a
Crearea unui editor propriu se realizeaz˘ cel mai simplu prin extinderea clasei
                                         a
utilitare AbstractCellEditor, care implementeaz˘ CellEditor, plus imple-
                                                     a
mentarea metodei specifice din TreeCellEditor.

public class MyEditor
    extends AbstractCellEditor
    implements TableCellEditor {

  // Singura metoda abstracta a parintelui
  public Object getCellEditorValue() {
    // Returneaza valoarea editata
    ...
  }
11.6. FOLOSIREA COMPONENTELOR                                            329

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


11.6.5     Arbori
Clasa JTree permite afi¸area unor elemente ˆ
                          s                     ıntr-o manier˘ ierarhic˘. Ca
                                                             a         a
orice component˘ Swing netrivial˘, un obiect JTree reprezint˘ doar o imagine
                 a               a                           a
a datelor, informatia ˆ sine fiind manipulat˘ prin intermediul unui model.
                   ¸ ın                       a
La nivel structural, un arbore este format dintr-o r˘d˘cin˘, noduri interne -
                                                     a a a
care au cel putin un fiu ¸i noduri frunz˘ - care nu mai au nici un descendent.
              ¸         s              a




    De¸i clasa JTree se g˘se¸te ˆ pachetul javax.swing, o serie de clase ¸i
       s                   a s ın                                          s
interfete necesare lucrului cu arbori se g˘sesc ˆ pachetul javax.swing.tree.
       ¸                                  a     ın
    Clasa care modeleaz˘ notiunea de nod al arborelui este DefaultMutable-
                        a ¸
TreeNode, aceasta fiind folosit˘ pentru toate tipurile de noduri. Crearea
                                  a
unui arbore presupune a¸adar crearea unui nod (r˘d˘cina), instantierea unui
                          s                        a a            ¸
obiect de tip JTree cu r˘d˘cina creat˘ ¸i ad˘ugarea apoi de noduri frunz˘ ca
                         a a           as     a                         a
fii ai unor noduri existente.

    String text = "<html><b>Radacina</b></html>";
    DefaultMutableTreeNode root = new DefaultMutableTreeNode(text);
    DefaultMutableTreeNode numere =
        new DefaultMutableTreeNode("Numere");
    DefaultMutableTreeNode siruri =
330                                               CAPITOLUL 11. SWING

       new DefaultMutableTreeNode("Siruri");

  for(int i=0; i<3; i++) {
    numere.add(new DefaultMutableTreeNode(new Integer(i)));
    siruri.add(new DefaultMutableTreeNode("Sirul " + i));
  }

  root.add(numere);
  root.add(siruri);

  JTree tree = new JTree(root);
    Dup˘ cum se observ˘, nodurile arborelui pot fi de tipuri diferite, reprezentarea
        a                 a
lor implicit˘ fiind obtinut˘prin apelarea metodei toString pentru obiectului
            a         ¸     a
continut. De asemenea, este posibil˘ specificarea unui text ˆ format HTML
    ¸                                a                       ın
ca valoare a unui nod, acesta fiind reprezentat ca atare.
    Dac˘ varianta ad˘ug˘rii explicite a nodurilor nu este potrivit˘, se poate
        a              a a                                          a
implementa o clas˘ care s˘ descrie modelul arborelui. Aceasta trebuie s˘
                    a         a                                               a
implementeze intefata TreeModel.
                      ¸
    Scopul unei componente de tip arbore este ˆ general selectarea unui nod
                                               ın
al ierarhiei. Ca ¸i ˆ cazul listelor sau a tabelelor, gestiunea elementelor
                   s ın
selectate se realizeaz˘ printr-un model, ˆ aceast˘ situatie interfata core-
                        a                 ın       a       ¸            ¸
spunz˘toare fiind TreeSelectionModel. Arborii permit ˆ
      a                                                     ınregistrarea unor
obiecte listener, de tip TreeSelectionListener, care s˘ trateze evenimentele
                                                       a
generate la schimbarea selectiei ˆ arbore.
                                ¸ ın
class Test implements TreeSelectionListener {
  ...
  public Test() {
    ...
    // Stabilim modul de selectie
    tree.getSelectionModel().setSelectionMode(
      TreeSelectionModel.SINGLE_TREE_SELECTION);

      // Adaugam un ascultator
      tree.addTreeSelectionListener(this);
      ...
  }
11.6. FOLOSIREA COMPONENTELOR                                             331

    public void valueChanged(TreeSelectionEvent e) {
      // Obtinem nodul selectat
      DefaultMutableTreeNode node = (DefaultMutableTreeNode)
             tree.getLastSelectedPathComponent();

      if (node == null) return;

      // Obtinem informatia din nod
      Object nodeInfo = node.getUserObject();
      ...
      }
}



   Fiecare nod al arborelui este reprezentar prin intermediul unei clase ren-
derer. Aceasta implementeaz˘ interfata TreeCellRenderer, cea folosit˘
                               a        ¸                                    a
implicit fiind DefaultTreeCellRenderer. Prin implementarea interfetei       ¸
sau extinderea clasei implicite pot fi create modalit˘¸i de personalizare a
                                                       at
nodurilor arborelui ˆ functie de tipul sau valoarea acestora.
                    ın    ¸
   Exist˘ ˆ a ¸i diverse metode de a schimba ˆ a¸i¸area unui arbore f˘r˘ s˘
         a ıns˘ s                              ınf˘t s                 aa a
cre˘m noi clase de tip TreeCellRenderer. Acestea sunt:
   a

    • setRootVisible - Specific˘ dac˘ r˘d˘cina e vizibil˘ sau nu;
                              a    a a a               a

    • setShowsRootHandles - Specific˘ dac˘ nodurile de pe primul nivel au
                                     a    a
      simboluri care s˘ permit˘ expandarea sau restrˆngerea lor.
                      a       a                     a

    • putClientProperty - Stabile¸te diverse propriet˘¸i, cum ar fi modul
                                      s                    at
      de reprezentare a relatiilor (liniilor) dintre nodurile p˘rinte ¸i fiu:
                            ¸                                  a      s

      tree.putClientProperty("JTree.lineStyle", "Angled");
      // sau "Horizontal", "None"

    • Specificarea unei iconite pentru nodurile frunz˘ sau interne:
                            ¸                       a

        ImageIcon leaf = createImageIcon("img/leaf.gif");
        ImageIcon open = createImageIcon("img/open.gif");
        ImageIcon closed = createImageIcon("img/closed.gif");
332                                              CAPITOLUL 11. SWING


         DefaultTreeCellRenderer renderer =
           new DefaultTreeCellRenderer();
         renderer.setLeafIcon(leaf);
         renderer.setOpenIcon(open);
         renderer.setClosedIcon(closed);

         tree.setCellRenderer(renderer);

11.6.6     Containere
Dup˘ cum ¸tim, containerele reprezint˘ suprafet de afi¸are pe care pot fi
    a       s                          a        ¸       s
plasate ale componente, eventual chiar alte containere. Superclasa compo-
nentelor de acest tip este Container, clas˘ despre care am mai discutat ˆ
                                          a                             ın
capitolul dedicat modeluli AWT.
   Containerele pot fi ˆ artite ˆ dou˘ categorii:
                       ımo˘ ¸ ın      a

  1. Containere de nivel ˆ   ınalt - Acestea sunt JFrame, JDialog, JApplet
     ¸i reprezint˘ r˘d˘cinile ierarhiilor de componente ale unei aplicatii.
     s           a a a                                                 ¸

  2. Containere intermediare - Reprezint˘ suprafete de afi¸are cu aju-
                                               a       ¸         s
     torul c˘rora pot fi organizate mai eficient componentele aplicatiei, putˆnd
            a                                                      ¸       a
     fi imbricate. Cele mai importante clase care descriu astfel de containere
     sunt:

         – JPanel
         – JScrollPane
         – JTabbedPane
         – JSplitPane
         – JLayeredPane
         – JDesktopPane
         – JRootPane

   JPanel are aceea¸i functionalitate ca ¸i clasa Panel din AWT, fiind folosit
                    s     ¸              s
pentru gruparea mai multor componente Swing ¸i plasarea lor ˆ
                                                    s              ımpreun˘ a
pe o alt˘ suprafat˘ de afi¸are. Gestionarul de pozitionare implicit este
        a        ¸a        s                             ¸
FlowLayout, acesta putˆnd fi schimbat ˆ a chiar ˆ momentul construirii
                        a                 ıns˘        ın
11.6. FOLOSIREA COMPONENTELOR                                             333

obiectului JPanel sau ulterior cu metoda setLayout. Ad˘ugarea de compo-
                                                          a
nente se realizeaz˘ ca pentru orice container, folosind metoda add.
                  a
  JPanel p = new JPanel(new BorderLayout());
  /* Preferabil, deoarece nu mai este construit si
     un obiect de tip FlowLayout (implicit)
  */
  p.add(new JLabel("Hello"));
  p.add(new JButton("OK"));
  ...



    JScrollPane este o clas˘ foarte important˘ ˆ arhitectura modelului
                            a                 a ın
Swing, deoarece ofer˘ suport pentru derularea pe orizontal˘ ¸i vertical˘ a
                    a                                      a s         a
componentelor a c˘ror reprezentare complet˘ nu ˆ
                  a                        a    ıncape ˆ suprafata aso-
                                                        ın         ¸
ciat˘, nici o component˘ Swing neoferind suport intrinsec pentru aceast˘
    a                  a                                                 a
operatie.
      ¸
  String elemente[] = new String[100];
  for(int i=0; i<100; i++)
   elemente[i] = "Elementul " + i;

  JList lista = new JList(elemente);
  JScrollPane sp = new JScrollPane(lista);
  frame.getContentPane().add(sp);




   JTabbedPane este util˘ pentru suprapunerea mai multor containere,
                             a
uzual panouri (obiecte de tip JPanel), pe acela¸i spatiu de afi¸are, selectarea
                                               s     ¸        s
334                                            CAPITOLUL 11. SWING

unuia sau altui panou realizˆndu-se prin intermediul unor butoane dispuse
                             a
pe partea superioar˘ a componentei, fiecare panou avˆnd un astfel de bu-
                    a                                 a
ton corespunz˘tor. Ca functionalitate, ofer˘ o implementare asem˘n˘toare
              a             ¸              a                     a a
gestionarului de pozitionare CardLayout.
                     ¸

  JTabbedPane tabbedPane = new JTabbedPane();
  ImageIcon icon = new ImageIcon("smiley.gif");

  JComponent panel1 = new JPanel();
  panel1.setOpaque(true);
  panel1.add(new JLabel("Hello"));
  tabbedPane.addTab("Tab 1", icon, panel1,
          "Aici avem o eticheta");
  tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);

  JComponent panel2 = new JPanel();
  panel2.setOpaque(true);
  panel2.add(new JButton("OK"));
  tabbedPane.addTab("Tab 2", icon, panel2,
          "Aici avem un buton");
  tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);




   JSplitPane permite crearea unui container care contine dou˘ compo-
                                                        ¸         a
nente dispuse fie una lˆng˘ cealalt˘, fie una sub alta ¸i separarea acestora
                      a a          a                 s
prin intermediul unei bare care s˘ permit˘ configurarea suprafetei alocate
                                 a       a                      ¸
fiec˘rei componente.
   a

  String elem[] = {"Unu", "Doi", "Trei" };
  JList list = new JList(elem);
11.6. FOLOSIREA COMPONENTELOR                                             335

  JPanel panel = new JPanel(new GridLayout(3, 1));
  panel.add(new JButton("Adauga"));
  panel.add(new JButton("Sterge"));
  panel.add(new JButton("Salveaza"));

  JTextArea text = new JTextArea(
      "Mai multe componente separate prinn" +
      "intermediul containerelor JSplitPane");

  // Separam lista de grupul celor trei butoane
  JSplitPane sp1 = new JSplitPane(
      JSplitPane.HORIZONTAL_SPLIT, list, panel);

  // Separam containerul cu lista si butoanele
  // de componenta pentru editare de text
  JSplitPane sp2 = new JSplitPane(
      JSplitPane.VERTICAL_SPLIT, sp1, text);

  frame.getContentPane().add(sp2);




11.6.7     Dialoguri
Clasa care descrie ferestre de dialog este JDialog, crearea unui dialog re-
alizˆndu-se prin extinderea acesteia, ˆ
    a                                 ıntocmai ca ˆ modelul AWT. In Swing
                                                   ın
exist˘ ˆ a o serie de clase predefinite ce descriu anumite tipuri de dialoguri,
      a ıns˘
extrem de utile ˆ majoritatea aplicatiilor. Acestea sunt:
                ın                    ¸

   • JOptionPane - Permite crearea unor dialoguri simple, folosite pentru
     afi¸area unor mesaje, realizarea unor interog˘ri de confirmare/renuntare,
       s                                         a                     ¸
336                                               CAPITOLUL 11. SWING

      etc. sau chiar pentru introducerea unor valori, clasa fiind extrem de
      configurabil˘. Mai jos, sunt exemplificate dou˘ modalit˘¸i de utilizare
                 a                                a          at
      a clasei:

         JOptionPane.showMessageDialog(frame,
             "Eroare de sistem !", "Eroare",
             JOptionPane.ERROR_MESSAGE);

         JOptionPane.showConfirmDialog(frame,
             "Doriti inchiderea aplicatiei ? ", "Intrebare",
             JOptionPane.YES_NO_OPTION,
             JOptionPane.QUESTION_MESSAGE);

   • JFileChooser - Dialog standard care permite navigarea prin sistemul
     de fi¸iere ¸i selectarea unui anumit fi¸ier pentru operatii de deschidere,
         s     s                          s                ¸
     respectiv salvare.

   • JColorChooser - Dialog standard pentru selectarea ˆ
                                                       ıntr-o manier˘
                                                                    a
     facil˘ a unei culori.
          a

   • ProgressMonitor - Clas˘ utilizat˘ pentru monitorizare˘ progresului
                              a        a                  a
     unei operatii consumatoare de timp.
               ¸


11.7      Desenarea
11.7.1     Metode specifice
Dup˘ cum ¸tim, desenarea unei componente este un proces care se executa
     a       s
automat ori de cˆte ori este necesar. Procesul ˆ sine este asem˘n˘tor celui
                  a                               ın                a a
din modelul AWT, ˆ a exist˘ unele diferente care trebuie mentionate.
                     ıns˘     a               ¸                   ¸
     Orice component˘ se g˘se¸te ˆ
                      a    a s ıntr-o ierarhie format˘ de containere, r˘d˘cina
                                                       a                 a a
acestei fiind un container de nivel ˆ ınalt, cum ar fi o fereastr˘ sau suprafata
                                                                a             ¸
unui applet. Cu alte cuvinte, componenta este plasat˘ pe o suprafat˘ de
                                                           a              ¸a
afi¸are, care la rˆndul ei poate fi plasat˘ pe alt˘ suprafat˘ ¸i a¸a mai departe.
   s             a                       a      a         ¸a s s
Cˆnd este necesar˘ desenarea componentei repsective, fie la prima sa afi¸are,
  a                a                                                       s
fie ca urmare a unor actiuni externe sau interne programului, operatia de de-
                        ¸                                             ¸
senare va fi executat˘ pentru toate containerele, ˆ
                      a                            ıncepˆnd cu cel de la nivelul
                                                        a
superior.
11.7. DESENAREA                                                        337

   Desenarea se bazeaz˘ pe modelul AWT, metoda cea mai important˘ fiind
                      a                                           a
paint, apelat˘ automat ori de cˆte ori este necesar. Pentru componentele
             a                 a
Swing, aceast˘ metod˘ are ˆ a o implementare specific˘ ¸i nu trebuie
             a       a     ıns˘                         a s
supradefinit˘. Aceasta este responsabil˘ cu apelul metodelor Swing ce
             a                           a
deseneaz˘ componenta ¸i anume:
        a             s

   • paintComponent - Este principala metod˘ pentru desenare ce este
                                               a
     supradefinit˘ pentru fiecare component˘ Swing ˆ parte pentru a descrie
                 a                         a      ın
     reprezentarea sa grafic˘. Implicit, ˆ cazul ˆ care componenta este
                           a            ın      ın
     opac˘ metoda deseneaz˘ suprafata sa cu culoarea de fundal, dup˘ care
         a                 a         ¸                             a
     va executa desenarea propriu-zis˘.
                                     a

   • paintBorder - Deseneaz˘ chenarele componentei (dac˘ exist˘). Nu
                           a                           a      a
     trebuie supradefinit˘.
                        a

   • paintChildren - Solicit˘ desenarea componentelor continute de aceast˘
                            a                            ¸               a
     component˘ (dac˘ exist˘). Nu trebuie supradefinit˘.
              a     a       a                         a

    Metoda paint este responsabil˘ cu apelul metodelor amintite mai sus ¸i
                                  a                                      s
realizarea unor optimiz˘ri legate de procesul de desenare, cum ar fi imple-
                         a
mentarea mecanismului de double-buffering. De¸i este posibil˘ supradefinirea
                                               s            a
ei, acest lucru nu este recomandat, din motivele amintite mai sus.
    Ca ¸i ˆ AWT, dac˘ se dore¸te redesenarea explicit˘ a unei componente
        s ın            a       s                      a
se va apela metoda repaint. In cazul ˆ care dimensiunea sau pozitia com-
                                       ın                          ¸
ponentei s-au schimbat, apelul metodei revalidate va precede apelul lui
repaint.


   Atentie
         ¸
   Intocmai ca ˆ AWT, desenarea este realizat˘ de firul de executie care se
                ın                               a               ¸
ocup˘ cu transmiterea evenimentelor. Pe perioada ˆ care acesta este ocupat
     a                                             ın
cu transmiterea unui mesaj nu va fi f˘cut˘ nici o desenare. De asemenea,
                                       a a
dac˘ acesta este blocat ˆ
   a                    ıntr-o operatiune de desenare ce consum˘ mult timp,
                                    ¸                          a
pe perioada respectiv˘ nu va fi transmis nici un mesaj.
                      a
338                                              CAPITOLUL 11. SWING

11.7.2     Consideratii generale
                    ¸
In continuare vom prezenta cˆteva consideratii generale legate de diferite
                               a             ¸
aspecte ale desen˘rii ˆ cadrul modelului Swing.
                 a ın


    Afi¸area imaginilor
       s
In AWT afi¸area unei imagini era realizat˘ uzual prin supradefinirea clasei
             s                            a
Canvas ¸i desenarea imaginii ˆ metoda paint a acesteia. In Swing, exist˘
         s                    ın                                            a
cˆteva solutii mai simple pentru afi¸area unei imagini, cea mai utilizat˘ fiind
 a          ¸                      s                                   a
crearea unei etichete (JLabel) sau a unui buton (JButton) care s˘ aib˘ se-
                                                                   a     a
tat˘ o anumit˘ imagine pe suprafata sa. Imaginea respectiv˘ trebuie creat˘
   a           a                   ¸                        a               a
folosind clasa ImageIcon.

  ImageIcon img = new ImageIcon("smiley.gif");
  JLabel label = new JLabel(img);



    Transparenta  ¸
Cu ajutorul metodei setOpaque poate fi controlat˘ opacitatea componentelor
                                                 a
Swing. Aceasta este o facilitate extrem de important˘ deoarece permite
                                                        a
crearea de componente care nu au form˘ rectangular˘. De exemplu, un
                                          a            a
buton circular va fi construit ca fiind transparent (setOpaque(false)) ¸i va
                                                                       s
desena ˆ interiorul s˘u o elips˘ umplut˘ cu o anumit˘ culoare. Evident, este
        ın           a         a        a            a
necesar˘ implementarea de cod specific pentru a trata ap˘sarea acestui tip
        a                                                 a
de buton.
    Trabsparenta ˆ a vine cu un anumit pret, deoarece pentru componentele
                ¸ ıns˘                      ¸
transparente vor trebui redesenate containerele pe care se g˘se¸te aceasta,
                                                             a s
ˆ
ıncetinind astfel procesul de afi¸are. Din acest motiv, de fiecare dat˘ cˆnd
                                 s                                  a a
este cazul, se recomand˘ setarea componentelor ca fiind opace
                        a
(setOpaque(true)).


    Dimensiunile componentelor
Dup˘ cum ¸tim, orice component˘ este definit˘ de o suprafat˘ rectangu-
     a       s                     a            a              ¸a
lar˘. Dimensiunile acestei pot fi obtinute cu metodele getSize, getWidth,
   a                               ¸
getHeight. Acestea includ ˆ a ¸i dimsniunile chenarelor, evident dac˘ aces-
                            ıns˘ s                                  a
tea exist˘. Suprafata ocupat˘ de acestea poate fi aflat˘ cu metoda getInsets
         a         ¸         a                       a
11.7. DESENAREA                                                          339

ce va returna un obiect de tip Insets ce specific˘ num˘rul de pixeli ocupati
                                                a    a                   ¸
cu chenare ˆ jurul componentei.
           ın
public void paintComponent(Graphics g) {
    ...
    Insets insets = getInsets();
    int currentWidth = getWidth() - insets.left - insets.right;
    int currentHeight = getHeight() - insets.top - insets.bottom;
    ...
}



    Contexte grafice
Argumentul metodei paintComponent este de tip Graphics ce ofer˘ prim-a
itivele standard de desenare. In majoritatea cazurilor ˆ a, argumentul este
                                                       ıns˘
de fapt de tip Graphics2D, clas˘ ce extinde Graphics ¸i pune la dispozitie
                                 a                       s               ¸
metode mai sofisitcate de desenare cunoscute sub numele de Java2D. Pentru
a avea acces la API-ul Java2D, este suficient s˘ facem conversia argumentului
                                              a
ce descrie contextul grafic:
public void paintComponent(Graphics g) {
  Graphics2D g2d = (Graphics2D)g;
  // Desenam apoi cu g2d
  ...
}
    In Swing, pentru a eficientiza desenarea, obiectul de tip Graphics primit
ca argument de metoda paintComponent este refolosit pentru desenarea com-
ponentei, a chenarelor ¸i a fiilor s˘i. Din acest motiv este foarte important
                       s           a
ca atunci cˆnd supradefinim metoda paintComponent s˘ ne asigur˘m c˘ la
           a                                             a          a    a
terminarea metodei starea obiectului Graphics este aceea¸i ca la ˆ
                                                             s       ınceput.
Acest lucru poate fi realizat fie explicit, fie folosind o copie a contextului
grafic primit ca argument:
// 1.Explicit
Graphics2D g2d = (Graphics2D)g;
g2d.translate(x, y);   // modificam contexul
...
g2d.translate(-x, -y); // revenim la starea initiala
340                                                 CAPITOLUL 11. SWING




// 2. Folosirea unei copii
Graphics2D g2d = (Graphics2D)g.create();
g2d.translate(x, y);
...
g2d.dispose();


11.8       Look and Feel
Prin sintagma ’Look and Feel’ (L&F) vom ˆ ¸elege modul ˆ care sunt de-
                                                 ınt              ın
senate componentele Swing ¸i felul ˆ care acestea interactioneaz˘ cu uti-
                                s        ın                       ¸     a
lizatorul. Posibilitatea de a alege ˆ  ıntre diferite moduri L&F are avantajul
de a oferi prezentarea unei aplicatii ˆ
                                     ¸ ıntr-o form˘ grafic˘ care s˘ corespund˘
                                                     a      a        a           a
preferintelor utilizatorilor. In principiu, variantele originale de L&F furnizate
        ¸
ˆ distributia standard ofereau modalitatea ca o interfat˘ Swing fie s˘ se
ın          ¸                                                  ¸a            a
ˆ
ıncadreze ˆ ansamblul grafic al sistemului de operare folosit, fie s˘ aib˘ un
           ın                                                          a    a
aspect specific Java.
    Orice L&F este descris de o clas˘ derivat˘ din LookAndFeel. Distributia
                                      a        a                               ¸
standard Java include urm˘toarele clase ce pot fi utilizate pentru selectarea
                              a
unui L&F:

   • javax.swing.plaf.metal.MetalLookAndFeel
     Este varianta implicit˘ de L&F ¸i are un aspect specific Java.
                           a        s

   • com.sun.java.swing.plaf.windows.WindowsLookAndFeel
     Varianta specific˘ sistemelor de operare Windows. Incepˆnd cu versi-
                      a                                     a
     unea 1.4.2 exist˘ ¸i implementarea pentru Windows XP .
                     as

   • com.sun.java.swing.plaf.mac.MacLookAndFeel
     Varianta specific˘ sistemelor de operare Mac.
                     a

   • com.sun.java.swing.plaf.motif.MotifLookAndFeel
     Specific˘ interfata CDE/Motif.
            a        ¸

   • com.sun.java.swing.plaf.gtk.GTKLookAndFeel
     GTK+ reprezint˘ un standard de creare a interfetelor grafice dezvoltat
                     a                              ¸
     independent de limbajul Java. (GTK este acronimul de la GNU Image
     Manipulation Program Toolkit). Folosind acest L&F este posibil˘ ¸i
                                                                      a s
11.8. LOOK AND FEEL                                                       341

     specificarea unei anumite teme prin intermediul unui fi¸ier de resurse
                                                          s
     sau folosind variabila swing.gtkthemefile, ca ˆ exemplul de mai jos:
                                                   ın

     java -Dswing.gtkthemefile=temaSpecifica/gtkrc App

  Specificare unei anumite interfete L&F poate fi realizat˘ prin mai multe
                                 ¸                      a
modalit˘¸i.
       at


    Folosirea clasei UImanager
Clasa UIManager pune la dispozitie o serie de metode statice pentru se-
                                   ¸
lectarea la momentul executiei a uni anumit L&F, precum ¸i pentru obtinerea
                          ¸                             s           ¸
unor variante specifice:
   • getLookAndFeel - Obtine varianta curent˘, returnˆnd un obiect de tip
                        ¸                   a        a
     LookAndFeel.
   • setLookAndFeel - Seteaz˘ modul curet L&F. Metoda prime¸te ca ar-
                              a                                 s
     gument un obiect dintr-o clas˘ derivat˘ din LookAndFeel, fie un ¸ir de
                                  a        a                        s
     caractere cu numele complet al clasei L&F.
   • getSystemLookAndFeelClassName - Obtine variant˘ specific˘ sistemu-
                                              ¸          a         a
     lui de operare folosit. In cazul ˆ care nu exist˘ nici o astfel de clas˘,
                                      ın             a                      a
     returneaz˘ varianta standard.
              a
   • getCrossPlatformLookAndFeelClassName - Returneaz˘ interfata grafic˘
                                                     a        ¸       a
     standard Java (JLF).
// Exemple:
UIManager.setLookAndFeel(
   "com.sun.java.swing.plaf.motif.MotifLookAndFeel");

UIManager.setLookAndFeel(
   UIManager.getSystemLookAndFeelClassName());




    Setarea propriet˘¸ii swing.defaultlaf
                      at
Exist˘ posibilitatea de a specifica varianta de L&F a aplicatie direct de la
      a                                                     ¸
linia de comand˘ prin setarea proprietˆ¸ii swing.defaultlaf:
                a                     at
342                                               CAPITOLUL 11. SWING

java -Dswing.defaultlaf=
      com.sun.java.swing.plaf.gtk.GTKLookAndFeel App
java -Dswing.defaultlaf=
      com.sun.java.swing.plaf.windows.WindowsLookAndFeel App

    O alt˘ variant˘ de a seta aceast˘ proprietate este schimbarea ei direct ˆ
         a        a                 a                                       ın
fi¸ierul swing.properties situat ˆ subdirectorul lib al distributiei Java.
 s                                 ın                             ¸

# Swing properties
swing.defaultlaf=
      com.sun.java.swing.plaf.windows.WindowsLookAndFeel

   Ordinea ˆ care este aleas˘ clasa L&F este urm˘toarea:
           ın               a                   a

  1. Apelul explicit al metodei UIManager.setLookAndFeel ˆ
                                                         ınaintea cre˘rii
                                                                     a
     unei componente Swing.

  2. Proprietatea swing.defaultlaf specificat˘ de la linia de comand˘.
                                            a                      a

  3. Proprietatea swing.defaultlaf specificat˘ ˆ fi¸ierul swing.properties.
                                            a ın s

  4. Clasa standard Java (JLF).



   Exist˘ posibilitatea de a schimba varianta de L&F chiar ¸i dup˘ afi¸area
        a                                                     s     a s
componentelor. Acesta este un proces care trebuie s˘ actualizeze ierarhiile
                                                      a
de componente, ˆ ıncepˆnd cu containerele de nivel ˆ
                       a                           ınalt ¸i va fi realizat prin
                                                         s
apelul metodei SwingUtilities.updateComponentTreeUI ce va primi ca
argument r˘d˘cina unei ierarhii. Secventa care efectueaz˘ aceast˘ operatie
           a a                          ¸                  a       a       ¸
pentru o fereastr˘ f este:
                 a

  UIManager.setLookAndFeel(numeClasaLF);
  SwingUtilities.updateComponentTreeUI(f);
  f.pack();
Capitolul 12

Fire de executie
             ¸

12.1       Introducere

Firele de executie fac trecerea de la programarea secvential˘ la programarea
                ¸                                          ¸ a
concurent˘. Un program secvential reprezint˘ modelul clasic de program: are
          a                      ¸            a
un ˆınceput, o secvent˘ de executie a instructiunilor sale ¸i un sfˆr¸it. Cu alte
                     ¸a          ¸           ¸             s       as
cuvinte, la un moment dat programul are un singur punct de executie. Un   ¸
program aflat ˆ executie se nume¸te proces. Un sistem de operare monotask-
               ın       ¸          s
ing, cum ar fi fi MS-DOS, nu este capabil s˘ execute decˆt un singur proces
                                             a               a
la un moment dat, ˆ timp ce un sistem de operare multitasking, cum ar fi
                     ın
UNIX sau Windows, poate rula oricˆte procese ˆ acela¸i timp (concurent),
                                      a            ın       s
folosind diverse strategii de alocare a procesorului fiec˘ruia dintre acestea.
                                                           a
Am reamintit acest lucru deoarece notiunea de fir de executie nu are sens
                                        ¸                        ¸
decˆt ˆ cadrul unui sistem de operare multitasking.
    a ın
   Un fir de executie este similar unui proces secvential, ˆ sensul c˘ are
                   ¸                                   ¸    ın        a
un ˆ
   ınceput, o secvent˘ de executie ¸i un sfˆr¸it. Diferenta dintre un fir de
                     ¸a          ¸ s       as
executie ¸i un proces const˘ ˆ faptul c˘ un fir de executie nu poate rula
     ¸ s                    a ın        a                 ¸
independent ci trebuie s˘ ruleze ˆ cadrul unui proces.
                        a        ın

   Definitie
        ¸
   Un fir de executie este o succesiune scevential˘ de instructiuni care se
                  ¸                          ¸ a             ¸
execut˘ ˆ cadrul unui proces.
      a ın


                                      343
344                                    CAPITOLUL 12. FIRE DE EXECUTIE
                                                                  ¸

            Program (proces)                  Program (proces)




     Un program ˆsi poate defini ˆ a nu doar un fir de executie ci oricˆte, ceea
                    ı¸                ıns˘                         ¸          a
ce ˆınseamn˘ c˘ ˆ cadrul unui proces se pot executa simultan mai multe fire
             a a ın
de executie, permitˆnd efectuarea concurent˘ a sarcinilor independente ale
           ¸           ¸a                             a
acelui program.
     Un fir de executie poate fi asem˘nat cu o versiune redus˘ a unui proces,
                       ¸                    a                        a
ambele rulˆnd simultan ¸i independent pe o structur˘ secvential˘ format˘ de
             a                s                               a      ¸ a            a
instructiunile lor. De asemenea, executia simultan˘ a firelor ˆ cadrul unui
        ¸                                     ¸              a         ın
proces este similar˘ cu executia concurent˘ a proceselor: sistemul de operare
                       a           ¸              a
va aloca procesorul dup˘ o anumit˘ strategie fiec˘rui fir de executie pˆn˘ la
                              a            a               a               ¸      a a
terminarea lor. Din acest motiv firele de executie mai sunt numite ¸i procese
                                                        ¸                    s
usoare.
     Care ar fi ˆ a deosebirile ˆ
                 ıns˘              ıntre un fir de executie ¸i un proces ? In primul,
                                                          ¸ s
rˆnd deosebirea major˘ const˘ ˆ faptul c˘ firele de executie nu pot rula decˆt
 a                          a      a ın          a              ¸                     a
ˆ cadrul unui proces. O alt˘ deosebire rezult˘ din faptul c˘ fiecare proces
ın                                 a                    a            a
are propria sa memorie (propriul s˘u spatiu de adrese) iar la crearea unui nou
                                          a     ¸
proces (fork) este realizat˘ o copie exact˘ a procesului p˘rinte: cod ¸i date,
                                a                a               a              s
ˆ timp ce la crearea unui fir nu este copiat decˆt codul procesului p˘rinte,
ın                                                        a                      a
toate firele de executie avˆnd acces la acelea¸i date, datele procesului original.
                         ¸     a                    s
A¸adar, un fir mai poate fi privit ¸i ca un context de executie ˆ cadrul unui
  s                                       s                        ¸ ın
proces.
     Firele de executie sunt utile ˆ multe privinte, ˆ a uzual ele sunt folosite
                       ¸               ın               ¸ ıns˘
pentru executarea unor operatii consumatoare de timp f˘r˘ a bloca procesul
                                    ¸                           aa
principal: calcule matematice, a¸teptarea eliber˘rii unei resurse, desenarea
                                        s                 a
componentelor unei aplicatii GUI, etc. De multe ori ori, firele ˆsi desf˘¸oar˘
                                 ¸                                      ı¸        as a
activitatea ˆ fundal ˆ a, evident, acest lucru nu este obligatoriu.
              ın           ıns˘


12.2        Crearea unui fir de executie
                                    ¸
Ca orice alt obiect Java, un fir de executie este o instant˘ a unei clase. Firele
                                        ¸                ¸a
de executie definite de o clas˘ vor avea acela¸i cod ¸i, prin urmare, aceea¸i
         ¸                    a                 s      s                      s
12.2. CREAREA UNUI FIR DE EXECUTIE
                               ¸                                          345

secventa de instructiuni. Crearea unei clase care s˘ defineasc˘ fire de executie
      ¸            ¸                               a         a             ¸
poate fi facut˘ prin dou˘ modalit˘¸i:
              a          a        at
    • prin extinderea clasei Thread.

    • prin implementarea interfetei Runnable.
                                ¸
    Orice clas˘ ale c˘rei instante vor fi executate separat ˆ
               a     a           ¸                         ıntr-un fir propriu
trebuie declarat˘ ca fiind de tip Runnable. Aceasta este o interfat˘ care
                 a                                                    ¸a
contine o singur˘ metod˘ ¸i anume metoda run. A¸adar, orice clas˘ ce descrie
    ¸            a        as                        s              a
fire de executie va contine metoda run ˆ care este implementat codul ce va fi
             ¸         ¸                ın
rulat. Interfata Runnable este conceput˘ ca fiind un protocol comun pentru
              ¸                           a
obiectele care doresc s˘ execute un anumit cod pe durata existentei lor.
                        a                                          ¸
    Cea mai important˘ clas˘ care implementeaz˘ interfata Runnable este
                         a     a                    a       ¸
Thread. Aceasta implementeaz˘ un fir de executie generic care, implicit, nu
                                   a              ¸
face nimic; cu alte cuvinte, metoda run nu contine nici un cod. Orice fir de
                                                ¸
executie este o instant˘ a clasei Thread sau a unei subclase a sa.
      ¸                ¸a

12.2.1     Extinderea clasei Thread
Cea mai simpl˘ metod˘ de a crea un fir de executie care s˘ realizeze o anumit˘
              a       a                         ¸        a                  a
actiune este prin extinderea clasei Thread ¸i supradefinirea metodei run a
  ¸                                          s
acesteia. Formatul general al unei astfel de clase este:
public class FirExcecutie extends Thread {

    public FirExcecutie(String nume) {
      // Apelam constructorul superclasei
      super(nume);
    }

    public void run() {
      // Codul firului de executie
      ...
    }
}
    Prima metod˘ a clasei este constructorul, care prime¸te ca argument un
                 a                                       s
¸ir ce va reprezenta numele firului de executie. In cazul ˆ care nu vrem s˘
s                                          ¸              ın             a
d˘m nume firelor pe care le cre˘m, atunci putem renunta la supradefinirea
  a                            a                        ¸
346                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

acestui constructor ¸i s˘ folosim constructorul implicit, f˘r˘ argumente, care
                    s a                                    aa
creeaz˘ un fir de executie f˘r˘ nici un nume. Ulterior, acesta poate primi un
      a                 ¸ aa
nume cu metoda setName. Evident, se pot defini ¸i alti constructori, ace¸tia
                                                    s ¸                     s
fiinde utili atunci cˆnd vrem s˘ trimitem diver¸i parametri de initializare
                    a            a                s                    ¸
firului nostru. A dou˘ metod˘ este metoda run, ”inima” oric˘rui fir de
                        a        a                                  a
executie, ˆ care scriem efectiv codul care trebuie s˘ se execute.
     ¸ ın                                            a
    Un fir de executie creat nu este automat pornit, lansarea s˘ fiind realizeaz˘
                  ¸                                           a               a
de metoda start, definit˘ ˆ clasa Thread.
                          a ın

    // Cream firul de executie
    FirExecutie fir = new FirExecutie("simplu");

    // Lansam in executie
    fir.start();

    S˘ consider˘m ˆ continuare un exemplu ˆ care definim un fir de executie
     a         a ın                         ın                        ¸
ce afi¸eaz˘ numerele ˆ
     s a            ıntregi dintr-un interval, cu un anumit pas.

                    Listing 12.1: Folosirea clasei Thread
class AfisareNumere extends Thread {
  private int a , b , pas ;

    public AfisareNumere ( int a , int b , int pas ) {
      this . a = a ;
      this . b = b ;
      this . pas = pas ;
    }

    public void run () {
      for ( int i = a ; i <= b ; i += pas )
        System . out . print ( i + " " ) ;
    }
}

public class TestThread {
  public static void main ( String args []) {
  AfisareNumere fir1 , fir2 ;

    fir1 = new AfisareNumere (0 , 100 , 5) ;
    // Numara de la 0 la 100 cu pasul 5

    fir2 = new AfisareNumere (100 , 200 , 10) ;
12.2. CREAREA UNUI FIR DE EXECUTIE
                               ¸                                            347

    // Numara de la 100 la 200 cu pasul 10

    fir1 . start () ;
    fir2 . start () ;
    // Pornim firele de executie
    // Ele vor fi distruse automat la terminarea lor
    }
}


    Gˆndind secvential, s-ar crede c˘ acest program va afi¸a prima dat˘ nu-
      a             ¸               a                      s            a
merele de la 0 la 100 cu pasul 5, apoi numerele de la 100 la 200 cu pasul 10,
ˆ
ıntrucˆt primul apel este c˘tre contorul fir1, deci rezultatul afi¸at pe ecran
       a                   a                                     s
ar trbui s˘ fie:
          a

0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100
100 110 120 130 140 150 160 170 180 190 200

    In realitate ˆ a, rezultatul obtinut va fi o intercalare de valori produse de
                 ıns˘              ¸
cele dou˘ fire ce ruleaz˘ simultan. La rul˘ri diferite se pot obtine rezultate
         a               a                  a                      ¸
diferite deoarece timpul alocat fiec˘rui fir de executie poate s˘ nu fie acela¸i,
                                     a                ¸          a            s
el fiind controlat de procesor ˆıntr-o manier˘ ”aparent” aleatoare. Un posibil
                                             a
rezultat al programului de mai sus:

0 100 5 110 10 120 15 130 20 140 25 150 160 170 180 190 200
30 35 40 45 50 55 60 65 70 75 80 85 90 95 100


12.2.2     Implementarea interfetei Runnable
                                ¸
Ce facem ˆ a cˆnd dorim s˘ cre˘m o clas˘ care instantiaz˘ fire de executie
          ıns˘ a             a    a           a           ¸ a              ¸
dar aceasta are deja o superclas˘, ¸tiind c˘ ˆ Java nu este permis˘ mo¸tenirea
                                a s        a ın                   a   s
multipl˘ ?
       a

    class FirExecutie extends Parinte, Thread // incorect !

In acest caz, nu mai putem extinde clasa Thread ci trebuie s˘ implement˘m
                                                              a             a
direct interfata Runnable. Clasa Thread implementeaz˘ ea ˆ a¸i interfata
              ¸                                          a     ıns˘s         ¸
Runnable ¸i, din acest motiv, la extinderea ei obtineam implementarea indi-
           s                                      ¸
rect˘ a interfetei. A¸adar, interfat˘ Runnable permite unei clase s˘ fie activ˘,
    a          ¸     s             ¸a                              a          a
f˘r˘ a extinde clasa Thread.
 aa
    Interfata Runnable se g˘se¸te ˆ pachetul java.lang ¸i este definit˘ astfel:
           ¸                a s ın                      s              a
348                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

public interface Runnable {
  public abstract void run();
}
Prin urmare, o clas˘ care instantiaz˘ fire de executie prin implementarea
                     a            ¸ a                ¸
interfetei Runnable trebuie obligatoriu s˘ implementeze metoda run. O astfel
       ¸                                 a
de clas˘ se mai nume¸te clas˘ activ˘ ¸i are urm˘toarea structur˘:
       a              s      a      as          a              a
public class ClasaActiva implements Runnable {

    public void run() {
      //Codul firului de executie
      ...
    }
}
    Spre deosebire de modalitatea anterioar˘, se pierde ˆ a tot suportul oferit
                                            a           ıns˘
de clasa Thread. Simpla instantiere a unei clase care implemeneaz˘ interfata
                                ¸                                   a        ¸
Runnable nu creeaz˘ nici un fir de executie, crearea acestora trebuind f˘cut˘
                    a                     ¸                              a a
explicit. Pentru a realiza acest lucru trebuie s˘ instantiem un obiect de tip
                                                a        ¸
Thread ce va reprezenta firul de executie propriu zis al c˘rui cod se gase¸te
                                        ¸                  a                s
ˆ clasa noastr˘. Acest lucru se realizeaz˘, ca pentru orice alt obiect, prin
ın             a                            a
instructiunea new, urmat˘ de un apel la un constructor al clasei Thread, ˆ a
       ¸                  a                                               ıns˘
nu la oricare dintre ace¸tia. Trebuie apelat constructorul care s˘ primeasc˘
                        s                                         a           a
drept argument o instant˘ a clasei noastre. Dup˘ creare, firul de executie
                          ¸a                        a                       ¸
poate fi lansat printr-un apel al metodei start.

    ClasaActiva obiectActiv = new ClasaActiva();
    Thread fir = new Thread(obiectActiv);
    fir.start();

    Aceste operatiuni pot fi f˘cute chiar ˆ cadrul clasei noastre:
                ¸            a           ın
public class FirExecutie implements Runnable {

    private Thread fir = null;

    public FirExecutie()
      if (fir == null) {
        fir = new Thread(this);
12.2. CREAREA UNUI FIR DE EXECUTIE
                               ¸                                           349

            fir.start();
        }
    }

    public void run() {
      //Codul firului de executie
      ...
    }
}

    Specificarea argumentului this ˆ constructorul clasei Thread determin˘
                                   ın                                     a
crearea unui fir de executie care, la lansarea sa, va apela metoda run din
                         ¸
clasa curent˘. A¸adar, acest constructor accept˘ ca argument orice instant˘
            a s                                a                         ¸a
a unei clase ”Runnable”. Pentru clasa FirExecutie dat˘ mai sus, lansarea
                                                         a
firului va fi f˘cut˘ automat la instantierea unui obiect al clasei:
             a a                    ¸

    FirExecutie fir = new FirExecutie();



    Atentie
          ¸
    Metoda run nu trebuie apelat˘ explicit, acest lucru realizˆndu-se automat
                                 a                            a
la apelul metodei start. Apelul explicit al metodei run nu va furniza nici o
eroare, ˆ a aceasta va fi executat˘ ca orice alt˘ metoda, ¸i nu separat ˆ
        ıns˘                     a             a          s            ıntr-un
fir.


    S˘ consider˘m urm˘torul
     a          a      a         exemplu ˆ care cre˘m dou˘ fire de executie
                                          ın         a      a               ¸
folosind interfata Runnable.
                ¸                Fiecare fir va desena figuri geometrice de un
anumit tip, pe o suprafat˘ de
                        ¸a       desenare de tip Canvas. Vom porni apoi dou˘  a
fire de executie care vor rula
             ¸                   concurent, desenˆnd figuri diferite, fiecare pe
                                                  a
suprafata sa.
        ¸

                    Listing 12.2: Folosirea interfetei Runnable
                                                   ¸
import java . awt .*;
import java . awt . event .*;

class Plansa extends Canvas implements Runnable {
  // Deoarece Plansa extinde Canvas ,
350                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

  // nu mai putem extinde clasa Thread

  Dimension dim = new Dimension (300 , 300) ;
  Color culoare ;
  String figura ;
  int x =0 , y =0 , r =0;

  public Plansa ( String figura , Color culoare ) {
    this . figura = figura ;
    this . culoare = culoare ;
  }

  public Dimension getPreferredSize () {
    return dim ;
  }

  public void paint ( Graphics g ) {
    // Desenam un chenar
    g . setColor ( Color . black ) ;
    g . drawRect (0 , 0 , dim . width -1 , dim . height -1) ;

      // Desenam figura la coordonatele calculate
      // de firul de executie
      g . setColor ( culoare ) ;
      if ( figura . equals ( " patrat " ) )
          g . drawRect (x , y , r , r ) ;
      else
      if ( figura . equals ( " cerc " ) )
          g . drawOval (x , y , r , r ) ;
  }

  public void update ( Graphics g ) {
    paint ( g ) ;
    // Supradefinim update ca sa nu mai
    // fie stearsa suprafata de desenare
  }

  public void run () {
    /* Codul firului de executie :
       Afisarea a 100 de figuri geometrice la pozitii
       si dimensiuni calculate aleator .
       Intre doua afisari , facem o pauza de 50 ms
    */

      for ( int i =0; i <100; i ++) {
12.2. CREAREA UNUI FIR DE EXECUTIE
                               ¸                                 351

            x = ( int ) ( Math . random () * dim . width ) ;
            y = ( int ) ( Math . random () * dim . height ) ;
            r = ( int ) ( Math . random () * 100) ;
            try {
              Thread . sleep (50) ;
            } catch ( InterruptedEx c e p t i o n e ) {}
            repaint () ;
        }
    }
}

class Fereastra extends Frame {
  public Fereastra ( String titlu ) {
    super ( titlu ) ;
    this . addWindowListener ( new WindowAdapter () {
       public void windowClosing ( WindowEvent e ) {
         System . exit (0) ;
       }
    }) ;

        // Cream doua obiecte active de tip Plansa
        Plansa p1 = new Plansa ( " patrat " , Color . blue ) ;
        Plansa p2 = new Plansa ( " cerc " , Color . red ) ;

        // Acestea extind Canvas , le plasam pe fereastra
        setLayout ( new GridLayout (1 , 2) ) ;
        add ( p1 ) ;
        add ( p2 ) ;
        pack () ;

        // Pornim doua fire de executie , care vor
        // actualiza desenul celor doua planse
        new Thread ( p1 ) . start () ;
        new Thread ( p2 ) . start () ;
    }
}

public class TestRunnable {
  public static void main ( String args []) {
    Fereastra f = new Fereastra ( " Test Runnable " ) ;
    f . show () ;
  }
}
352                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

12.3       Ciclul de viat˘ al unui fir de executie
                        ¸a                    ¸
Fiecare fir de executie are propriul s˘u ciclu de viat˘: este creat, devine activ
                     ¸               a              ¸a
prin lansarea sa ¸i, la un moment dat, se termin˘. In continuare, vom analiza
                 s                               a
mai ˆındeaproape st˘rile ˆ care se poate g˘si un fir de executie. Diagrama
                      a     ın               a                   ¸
de mai jos ilustreaz˘ generic aceste st˘ri precum ¸i metodele care provoaca
                      a                a            s
tranzitia dintr-o stare ˆ alta:
      ¸                  ın




    A¸adar, un fir de executie se poate g˘si ˆ una din urm˘toarele patru
       s                  ¸             a ın             a
st˘ri:
  a

   • ”New Thread”

   • ”Runnable”

   • ”Not Runnable”

   • ”Dead”



    Starea ”New Thread”
Un fir de executie se g˘se¸te ˆ aceast˘ stare imediat dup˘ crearea sa, cu alte
              ¸       a s ın         a                  a
cuvinte dup˘ instantierea unui obiect din clasa Thread sau dintr-o subclas˘
           a        ¸                                                       a
a sa.

  Thread fir = new Thread(obiectActiv);
  // fir se gaseste in starea "New Thread"
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                               353

In aceast˘ stare firul este ”vid”, el nu are alocate nici un fel de resurse sis-
          a
tem ¸i singura operatiune pe care o putem executa asupra lui este lansarea
     s                 ¸
ˆ executie, prin metoda start. Apelul oric˘rei alte metode ˆ afar˘ de start
ın      ¸                                     a               ın   a
nu are nici un sens ¸i va provoca o exceptie de tipul IllegalThreadStateException.
                    s                    ¸


   Starea ”Runnable”
Dup˘ apelul metodei start un fir va trece ˆ starea ”Runnable”, adic˘ va fi
    a                                    ın                       a
ˆ executie.
ın     ¸

  fir.start();
  //fir se gaseste in starea "Runnable"

Metoda start realizez˘ urm˘toarele operatiuni necesare rul˘rii firului de
                     a    a             ¸                 a
executie:
     ¸

   • Aloc˘ resursele sistem necesare.
         a

   • Planific˘ firul de executie la procesor pentru a fi lansat.
            a              ¸

   • Apeleaz˘ metoda run a obiectului activ al firului.
            a

    Un fir aflat ˆ starea ”Runnable” nu ˆ
                 ın                        ınseamn˘ neap˘rat c˘ se g˘¸este
                                                   a       a     a      as
efectiv ˆ executie, adic˘ instructiunile sale sunt interpretate de procesor.
         ın      ¸       a        ¸
Acest lucru se ˆ ampl˘ din cauza c˘ majoritatea calculatoarelor au un singur
               ıntˆ    a            a
procesor iar acesta nu poate rula simultan toate firele de executie care se
                                                                   ¸
gasesc ˆ starea ”Runnable”. Pentru a rezolva aceasta problem˘ exist˘ o
        ın                                                         a       a
planificare care s˘ partajeze dinamic ¸i corect procesorul ˆ
                  a                   s                   ıntre toate firele de
executie care sunt ˆ starea ”Runnable”. A¸adar, un fir care ”ruleaz˘” poate
      ¸             ın                     s                         a
s˘-¸i a¸tepte de fapt rˆndul la procesor.
 as s                  a


    Starea ”Not Runnable”
Un fir de executie poate ajunge ˆ aceaat˘ stare ˆ una din urm˘toarele
              ¸                ın      a       ın           a
situatii:
     ¸

   • Este ”adormit” prin apelul metodei sleep;

   • A apelat metoda wait, a¸teptˆnd ca o anumit˘ conditie s˘ fie satisfa-
                            s    a              a      ¸ a
     cut˘;
        a
354                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

   • Este blocat ˆ
                 ıntr-o operatie de intrare/ie¸ire.
                             ¸                s

   Metoda sleep este o metod˘ static˘ a clasei Thread care provoac˘ o
                                a       a                                a
pauz˘ ˆ timpul rul˘rii firului curent aflat ˆ executie, cu alte cuvinte ˆ
    a ın             a                      ın       ¸                     ıl
”adoarme” pentru un timp specificat. Lungimea acestei pauze este specificat˘ a
ˆ milisecunde ¸i chiar nanosecunde. Intrucˆt poate provoca exceptii de tipul
ın            s                           a                      ¸
InterruptedException, apelul acestei metode se face ˆ   ıntr-un bloc de tip
try-cacth:

  try {
    // Facem pauza de o secunda
    Thread.sleep(1000);
  } catch (InterruptedException e) {
    ...
  }

Observati c˘ metoda fiind static˘ apelul ei nu se face pentru o instant˘ anume
         ¸ a                     a                                    ¸a
a clasei Thread. Acest lucru este foarte normal deoarece, la un moment dat,
un singur fir este ˆ executie ¸i doar pentru acesta are sens ”adormirea” sa.
                     ın     ¸ s
    In intervalul ˆ care un fir de executie ”doarme”, acesta nu va fi execut
                   ın                     ¸
chiar dac˘ procesorul devine disponibil. Dup˘ expirarea intervalului specifi-
          a                                     a
cat firul revine ˆ starea ”Runnable” iar dac˘ procesorul este ˆ continuare
                  ın                            a                 ın
disponibil ˆ va continua executia.
            ısi                  ¸
    Pentru fiecare tip de intrare ˆ starea ”Not Runnable”, exist˘ o secvent˘
                                  ın                               a         ¸a
specific˘ de ie¸ire din starea repectiv˘, care readuce firul de executie ˆ starea
        a       s                     a                             ¸ ın
”Runnable”. Acestea sunt:

   • Dac˘ un fir de executie a fost ”adormit”, atunci el devine ”Runnable”
        a               ¸
     doar dup˘ scurgerea intervalului de timp specificat de instructiunea
             a                                                     ¸
     sleep.

   • Dac˘ un fir de executie a¸teapt˘ o anumit˘ conditie, atunci un alt
         a                  ¸    s     a           a       ¸
     obiect trebuie s˘ ˆ informeze dac˘ acea conditie este ˆ
                     a ıl                a            ¸       ındeplinit˘ sau
                                                                        a
     nu; acest lucru se realizeaz˘ prin instructiunile notify sau notifyAll
                                 a             ¸
     (vezi ”Sincronizarea firelor de executie”).
                                           ¸

   • Dac˘ un fir de executie este blocat ˆ
         a                ¸             ıntr-o operatiune de intrare/ie¸ire
                                                    ¸                  s
     atunci el redevine ”Runnable” atunci cˆnd acea operatiune s-a termi-
                                           a              ¸
     nat.
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                                355

    Starea ”Dead”
Este starea ˆ care ajunge un fir de executie la terminarea sa. Un fir nu
             ın                              ¸
poate fi oprit din program printr-o anumit˘ metod˘, ci trebuie s˘ se termine
                                           a        a            a
ˆ mod natural la ˆ
ın                ıncheierea metodei run pe care o execut˘. Spre deosebire de
                                                         a
versiunile curente ale limbajului Java, ˆ versiunile mai vechi exista metoda
                                        ın
stop a clasei Thread care termina fortat un fir de executie, ˆ a aceasta a
                                        ¸                  ¸ ıns˘
fost eliminat˘ din motive de securitate. A¸adar, un fir de executie trebuie
             a                              s                      ¸
s˘-¸i ”aranjeze” singur propria sa ”moarte”.
 as

12.3.1     Terminarea unui fir de executie
                                      ¸
Dup˘ cum am vazut, un fir de executie nu poate fi terminat fortat de c˘tre
    a                                ¸                         ¸       a
program ci trebuie s˘-¸i ”aranjeze” singur terminarea sa. Acest lucru poate
                    as
fi realizat ˆ dou˘ modalit˘¸i:
           ın   a         at

   1. Prin scrierea unor metode run care s˘-¸i termine executia ˆ mod nat-
                                          as                ¸ ın
      ural. La terminarea metodei run se va termina automat ¸i firul de
                                                                  s
      executie, acesta intrˆnd ˆ starea Dead. Ambele exemple anteriorare
            ¸              a ın
      se ˆ
         ıncadreaz˘ ˆ aceast˘ categorie.
                   a ın      a

         // Primul exemplu
         public void run() {
           for(int i = a; i <= b; i += pas)
             System.out.print(i + " " );
         }

     Dup˘ afi¸area numerelor din intervalul specificat, metoda se termin˘
          a s                                                         a
     ¸i, odat˘ cu ea, se va termina ¸i firul de executie repsectiv.
     s       a                      s               ¸

   2. Prin folosirea unei variabile de terminare. In cazul cˆnd metoda run
                                                              a
      trebuie s˘ execute o bucl˘ infinit˘ atunci aceasta trebuie controlat˘
               a                  a       a                                   a
      printr-o variabil˘ care s˘ opreasc˘ ciclul atunci cˆnd dorim ca firul de
                       a       a        a                a
      executie s˘ se termine. Uzual, vom folosi o variabil˘ membr˘ a clasei
            ¸ a                                             a        a
      care descrie firul de executie care fie este public˘, fie este asociat˘ cu o
                                 ¸                     a                 a
      metod˘ public˘ care permite schimbarea valorii sale.
             a       a

   S˘ consider˘m exemplul unui fir de executie care trebuie s˘ numere se-
    a          a                              ¸             a
cundele scurse pˆn˘ la ap˘sarea tastei Enter.
                 a a     a
356                                   CAPITOLUL 12. FIRE DE EXECUTIE
                                                                 ¸

                Listing 12.3: Folosirea unei variabile de terminare
import java . io .*;

class NumaraSecunde extends Thread {
  public int sec = 0;
  // Folosim o variabila de terminare
  public boolean executie = true ;

    public void run () {
      while ( executie ) {
        try {
          Thread . sleep (1000) ;
          sec ++;
          System . out . print ( " . " ) ;
        } catch ( Interr u pt ed Ex ce pt io n e ) {}
      }
    }
}

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

        NumaraSecunde fir = new NumaraSecunde () ;
        fir . start () ;

        System . out . println ( " Apasati tasta Enter " ) ;
        System . in . read () ;

        // Oprim firul de executie
        fir . executie = false ;
        System . out . println ( "S - au scurs " + fir . sec + " secunde " ) ;
    }
}




    Nu este necesar˘ distrugerea explicit˘ a unui fir de executie. Sistemul
                    a                    a                     ¸
Java de colectare a ”gunoiului” se ocup˘ de acest lucru. Setarea valorii
                                          a
null pentru variabila care referea instanta firului de executie va u¸ura ˆ a
                                         ¸                 ¸       s    ıns˘
activitatea procesului gc.
    Metoda System.exit va oprit fortat toate firele de executie ¸i va termina
                                   ¸                       ¸ s
aplicatia curent˘.
      ¸         a
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                                  357

   Pentru a testa dac˘ un fir de executie a fost pornit dar nu s-a terminat
                     a               ¸
ˆ a putem folosi metoda isAlive. Metoda returneaz˘:
ınc˘                                                a

   • true - dac˘ firul este ˆ una din st˘rile ”Runnable” sau ”Not Runnable”
               a           ın          a

   • false - dac˘ firul este ˆ una din starile ”New Thread” sau ”Dead”
                a           ın

   Intre st˘rile ”Runnable” sau ”Not Runnable”, repectiv ”New Thread”
           a
sau ”Dead” nu se poate face nici o diferentiere.
                                          ¸

     NumaraSecunde fir = new NumaraSecunde();
     // isAlive retuneaza false (starea este New Thread)

     fir.start();
     // isAlive retuneaza true (starea este Runnable)

     fir.executie = false;
     // isAlive retuneaza false (starea este Dead)

12.3.2      Fire de executie de tip ”daemon”
                         ¸
Un proces este considerat ˆ executie dac˘ contine cel putin un fir de executie
                           ın       ¸     a      ¸          ¸                 ¸
activ. Cu alte cuvinte, la rularea unei aplicatii, ma¸ina virtual˘ Java nu se va
                                              ¸      s            a
opri decˆt atunci cˆnd nu mai exist˘ nici un fir de executie activ. De multe ori
         a         a                 a                    ¸
ˆ a dorim s˘ folosim fire care s˘ realizeze diverse activit˘¸i, eventual periodic,
ıns˘        a                   a                         at
pe toat˘ durata de executie a programului iar ˆ momentul termin˘rii acestuia
        a                ¸                      ın                    a
s˘ se termine automat ¸i firele respective. Aceste fire de executie se numesc
 a                      s                                           ¸
demoni.
    Dup˘ crearea sa, un fir de executie poate fi f˘cut demon, sau scos din
         a                             ¸             a
aceast˘ stare, cu metoda setDaemon.
       a

          Listing 12.4: Crearea unui fir de excutie de tip ”daemon”
                                               ¸
class Beeper implements Runnable {
  public void run () {
    while ( true ) {
      java . awt . Toolkit . getDef aultToo lkit () . beep () ;
      try {
        Thread . sleep (1000) ;
      } catch ( InterruptedEx c e p t i o n e ) {}
    }
358                                    CAPITOLUL 12. FIRE DE EXECUTIE
                                                                  ¸

     }
}

public class TestDaemon {
  public static void main ( String args [])
      throws java . io . IOException {

         Thread t = new Thread ( new Beeper () ) ;
         t . setDaemon ( true ) ;
         t . start () ;

         System . out . println ( " Apasati Enter ... " ) ;
         System . in . read () ;

         // " Demonul " se termina automat
         // la terminarea aplicatiei
     }
}



12.3.3         Stabilirea priorit˘¸ilor de executie
                                 at             ¸
Majoritatea calculatoarelor au un sigur procesor, ceea ce ˆ ınseamn˘ c˘ firele
                                                                    a a
de executie trebuie s˘-¸i ˆ
         ¸           a s ımpart˘ accesul la acel procesor. Executia ˆ
                               a                                  ¸ ıntr-o an-
umit˘ ordine a mai multor fire de executie pe un num˘r limitat de procesoare
     a                                 ¸              a
se nume¸te planificare (scheduling). Sistemul Java de executie a programelor
        s                                                    ¸
implementeaz˘ un algoritm simplu, determinist de planificare, cunoscut sub
              a
numele de planificare cu priorit˘¸i fixate.
                               at
    Fiecare fir de executie Java prime¸te la crearea sa o anumit˘ priori-
                          ¸             s                            a
tate. O prioritate este de fapt un num˘r ˆ a ıntreg cu valori cuprinse ˆ ıntre
MIN PRIORITY ¸i MAX PRIORITY. Implicit, prioritatea unui fir nou creat are
                s
valoarea NORM PRIORITY. Aceste trei constante sunt definite ˆ clasa Thread
                                                               ın
astfel:
    public static final int MAX_PRIORITY = 10;
    public static final int MIN_PRIORITY = 1;
    public static final int NORM_PRIORITY= 5;
Schimbarea ulterioar˘ a priorit˘¸ii unui fir de executie se realizeaz˘ cu metoda
                    a          at                   ¸               a
setPriority a clasei Thread.
   La nivelul sistemului de operare, exist˘ dou˘ modele de lucru cu fire de
                                            a     a
executie:
     ¸
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                                359

   • Modelul cooperativ, ˆ care firele de executie decid cˆnd s˘ cedeze pro-
                         ın                    ¸         a    a
     cesorul; dezavantajul acestui model este c˘ unele fire pot acapara pro-
                                               a
     cesorul, nepermitˆnd ¸i executia altora pˆn˘ la terminarea lor.
                     ¸a s          ¸          a a
   • Modelul preemptiv, ˆ care firele de executie pot fi ˆ
                          ın                    ¸         ıntrerupte oricˆnd,
                                                                         a
     dup˘ ce au fost l˘sate s˘ ruleze o perioad˘, urmˆnd s˘ fie reluate dup˘
          a            a     a                 a      a     a               a
     ce ¸i celelalte fire aflate ˆ executie au avut acces la procesor; acest
         s                     ın       ¸
     sistem se mai nume¸te cu ”cuante de timp”, dezavantajul s˘u fiind
                           s                                         a
     nevoia de a sincroniza accesul firelor la resursele comune.
A¸adar, ˆ modelul cooperativ firele de executie sunt responsabile cu par-
   s        ım                                   ¸
tajarea timpului de executie, ˆ timp ce ˆ modelul preemptiv ele trebuie
                            ¸ ın             ın
s˘ partajeze resursele comune. Deoarece specificatiile ma¸inii virtuale Java
 a                                                  ¸       s
nu impun folosirea unui anumit model, programele Java trebuie scrise astfel
ˆ at s˘ functioneze corect pe ambele modele. In continuare, vom mai detalia
ıncˆ a         ¸
putin aceste aspecte.
    ¸
     Planificatorul Java lucreaz˘ ˆ modul urmator: dac˘ la un moment dat
                                 a ın                    a
sunt mai multe fire de executie ˆ starea ”Runnable”, adic˘ sunt pregatite
                                ¸ ın                          a
pentru a fi rulate, planificatorul ˆ va alege pe cel cu prioritatea cea mai
                                      ıl
mare pentru a-l executa. Doar cˆnd firul de executie cu prioritate maxim˘ se
                                   a               ¸                       a
termin˘, sau este suspendat din diverse motive, va fi ales un fir cu o prioritate
         a
mai mic˘. In cazul ˆ care toate firele au aceea¸i prioritate ele sunt alese pe
           a         ın                          s
rˆnd, dup˘ un algoritm simplu de tip ”round-robin”. De asemenea, dac˘
 a           a                                                                a
un fir cu prioritate mai mare decˆt firul care se execut˘ la un moment dat
                                     a                   a
solicit˘ procesorul, atunci firul cu prioritate mai mare este imediat trecut ˆ
       a                                                                     ın
executie iar celalalt trecut ˆ asteptare. Planificatorul Java nu va ˆ
       ¸                     ın                                      ıntrerupe
ˆ a un fir de executie ˆ favoarea altuia de aceeasi prioritate, ˆ a acest lucru
ıns˘                ¸ ın                                       ıns˘
ˆ poate face sistemul de operare ˆ cazul ˆ care acesta aloc˘ procesorul ˆ
ıl                                   ın      ın                a             ın
cuante de timp (un astfel de SO este Windows).
     A¸adar, un fir de executie Java cedeaz˘ procesorul ˆ una din situatiile:
      s                      ¸              a           ın
   • un fir de executie cu o prioritate mai mare solicit˘ procesorul;
                   ¸                                   a
   • metoda sa run se termin˘;
                            a
   • face explicit acest lucru apelˆnd metoda yield;
                                   a
   • timpul alocat pentru executia s˘ a expirat (pe SO cu cuante de timp).
                               ¸ a
360                                 CAPITOLUL 12. FIRE DE EXECUTIE
                                                               ¸

    Atentie
          ¸
    In nici un caz corectitudinea unui program nu trebuie s˘ se bazeze pe
                                                              a
mecansimul de planificare a firelor de executie, deoarece acesta poate fi diferit
                                          ¸
de la un sistem de operare la altul.


    Un fir de executie de lung˘ durat˘ ¸i care nu cedeaz˘ explicit procesorul la
                    ¸          a      as                a
anumite intervale de timp astfel ˆ at s˘ poata fi executate ¸i celelalte fire de
                                   ıncˆ a                      s
executie se nume¸te fir de executie egoist. Evident, trebuie evitat˘ scrierea lor
      ¸           s              ¸                                  a
ˆ
ıntrucˆt acapareaz˘ pe termen nedefinit procesorul, blocˆnd efectiv executia
      a             a                                      a                 ¸
celorlalte fire de executie pˆn˘ la terminarea sa. Unele sistemele de oper-
                         ¸    a a
are combat acest tip de comportament prin metoda aloc˘rii procesorului ˆ
                                                             a                ın
cuante de timp fiec˘rui fir de executie, ˆ a nu trebuie s˘ ne baz˘m pe acest
                     a                ¸ ıns˘               a        a
lucru la scrierea unui program. Un fir de executie trebuie s˘ fie ”corect”
                                                    ¸             a
fat˘de celelalte fire ¸i s˘ cedeze periodic procesorul astfel ˆ at toate s˘ aib˘
  ¸a                  s a                                    ıncˆ        a     a
posibilitatea de a se executa.


              Listing 12.5: Exemplu de fir de executie ”egoist”
                                                  ¸
class FirEgoist extends Thread {
  public FirEgoist ( String name ) {
    super ( name ) ;
  }
  public void run () {
    int i = 0;
    while ( i < 100000) {
      // Bucla care acapareaza procesorul
      i ++;
      if ( i % 100 == 0)
        System . out . println ( getName () + " a ajuns la " + i ) ;
      // yield () ;
    }
  }
}

public class TestFirEgoist {
  public static void main ( String args []) {
    FirEgoist s1 , s2 ;
    s1 = new FirEgoist ( " Firul 1 " ) ;
    s1 . setPriority ( Thread . MAX_PRIORITY ) ;
    s2 = new FirEgoist ( " Firul 2 " ) ;
    s2 . setPriority ( Thread . MAX_PRIORITY ) ;
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                              361

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


    Firul de executie s1 are prioritate maxim˘ ¸i pˆn˘ nu-¸i va termina
                   ¸                            a s a a        s
executia nu-i va permite firului s2 s˘ execute nici o instructiune, acaparˆnd
      ¸                             a                       ¸            a
efectiv procesorul. Rezultatul va ar˘ta astfel:
                                    a

    Firul    1 a ajuns la 100
    Firul    1 a ajuns la 200
    Firul    1 a ajuns la 300
    ...
    Firul    1   a   ajuns   la   99900
    Firul    1   a   ajuns   la   100000
    Firul    2   a   ajuns   la   100
    Firul    2   a   ajuns   la   200
    ...
    Firul    2 a ajuns la 99900
    Firul    2 a ajuns la 100000

Rezolvarea acestei probleme se face fie prin intermediul metodei statice yield
a clasei Thread, care determin˘ firul de executie curent s˘ se opreasc˘ tem-
                                a               ¸          a           a
porar, dˆnd ocazia ¸i altor fire s˘ se execute, fie prin ”adormirea” temporar˘
         a          s            a                                          a
a firului curent cu ajutorul metodei sleep. Prin metoda yield un fir de
executie nu cedeaz˘ procesorul decˆt firelor de executie care au aceea¸i pri-
      ¸             a                a                 ¸                s
oritate cu a sa ¸i nu celor cu priorit˘¸i mai mici. Decomentˆnd linia ˆ care
                s                     at                     a         ın
apel˘m yeld din exemplul anterior, executia celor dou˘ fire se va intercala.
    a                                       ¸            a

    ...
    Firul    1   a   ajuns   la   31900
    Firul    1   a   ajuns   la   32000
    Firul    2   a   ajuns   la   100
    Firul    1   a   ajuns   la   32100
    Firul    2   a   ajuns   la   200
    Firul    2   a   ajuns   la   300
    ...
362                                  CAPITOLUL 12. FIRE DE EXECUTIE
                                                                ¸

12.3.4      Sincronizarea firelor de executie
                                         ¸
Pˆn˘ acum am v˘zut cum putem crea fire de executie independente ¸i as-
  a a              a                                         ¸                 s
incrone, cu alte cuvinte care nu depind ˆ nici un fel de executia sau de
                                               ın                         ¸
rezultatele altor fire. Exist˘ ˆ a numeroase situatii cˆnd fire de executie
                                a ıns˘                     ¸ a                   ¸
separate, dar care ruleaz˘ concurent, trebuie s˘ comunice ˆ
                             a                         a            ıntre ele pentru
a accesa diferite resurse comune sau pentru a-¸i transmite dinamic rezul-
                                                        s
tatele ”muncii” lor. Cel mai elocvent scenariu ˆ care firele de executie
                                                          ın                     ¸
trebuie s˘ se comunice ˆ
           a               ıntre ele este cunoscut sub numele de problema pro-
duc˘torului/consumatorului, ˆ care produc˘torul genereaz˘ un flux de date
    a                            ın              a                a
care este preluat ¸i prelucrat de c˘tre consumator.
                   s                  a
    S˘ consider˘m de exemplu o aplicatie Java ˆ care un fir de executie (pro-
      a          a                         ¸         ın                      ¸
duc˘torul) scrie date ˆ
    a                    ıntr-un fi¸ier ˆ timp ce alt fir de executie (consuma-
                                    s ın                              ¸
torul) cite¸te date din acela¸i fi¸ier pentru a le prelucra. Sau, s˘ presupunem
             s                 s s                                    a
c˘ produc˘torul genereaz˘ ni¸te numere ¸i le plaseaz˘, pe rˆnd, ˆ
 a          a                a s             s             a     a     ıntr-un buffer
iar consumatorul cite¸te numerele din acel buffer pentru a le procesa. In am-
                       s
bele cazuri avem de-a face cu fire de executie concurente care folosesc o
                                                    ¸
resurs˘ comun˘: un fi¸ier, respectiv o zon˘ de memorie ¸i, din acest motiv,
        a        a       s                     a               s
ele trebuie sincronizate ˆ ıntr-o manier˘ care s˘ permit˘ decurgerea normal˘ a
                                          a       a          a                   a
activit˘¸ii lor.
        at


12.3.5      Scenariul produc˘tor / consumator
                            a
Pentru a ˆ
         ıntelege mai bine modalitatea de sincronizare a dou˘ fire de executie
                                                            a             ¸
s˘ implement˘m efectiv o problem˘ de tip produc˘tor/consumator. S˘ con-
 a            a                   a                a                   a
sider˘m urm˘toarea situatie:
     a       a            ¸

   • Produc˘torul genereaz˘ numerele ˆ
            a               a            ıntregi de la 1 la 10, fiecare la un
     interval neregulat cuprins ˆ
                                ıntre 0 ¸i 100 de milisecunde. Pe m˘sura ce
                                        s                           a
     le genereaz˘ ˆ
                a ıncearc˘ s˘ le plaseze ˆ
                          a a             ıntr-o zon˘ de memorie (o variabil˘
                                                    a                       a
     ˆ
     ıntreaga) de unde s˘ fie citite de c˘tre consumator.
                         a              a

   • Consumatorul va prelua, pe rˆnd, numerele generate de c˘tre pro-
                                     a                      a
     duc˘tor ¸i va afi¸a valoarea lor pe ecran.
        a    s       s

Pentru a fi accesibil˘ ambelor fire de executie, vom ˆ
                    a                     ¸        ıncapsula variabila ce va
contine numerele generate ˆ
                          ıntr-un obiect descris de clasa Buffer ¸i care va
                                                                  s
avea dou˘ metode put (pentru punerea unui numar ˆ buffer) ¸i get (pentru
         a                                          ın       s
obtinerea numarului din buffer).
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                              363

    F˘r˘ a folosi nici un mecanism de sincronizare clasa Buffer arat˘ astfel:
     aa                                                             a

                 Listing 12.6: Clasa Buffer f˘r˘ sincronizare
                                             aa
class Buffer {
  private int number = -1;

    public int get () {
      return number ;
    }

    public void put ( int number ) {
      this . number = number ;
    }
}

    Vom implementa acum clasele Producator ¸i Consumator care vor descrie
                                              s
cele dou˘ fire de executie. Ambele vor avea o referinta comun˘ la un obiect
        a             ¸                             ¸        a
de tip Buffer prin intermediul c˘ruia ˆsi comunic˘ valorile.
                                a     ı¸         a

               Listing 12.7: Clasele Producator ¸i Consumator
                                                s
class Producator extends Thread {
  private Buffer buffer ;

    public Producator ( Buffer b ) {
      buffer = b ;
    }

    public void run () {
      for ( int i = 0; i < 10; i ++) {
        buffer . put ( i ) ;
        System . out . println ( " Producatorul a pus : t " + i ) ;
        try {
          sleep (( int ) ( Math . random () * 100) ) ;
        } catch ( InterruptedE x c e p t i o n e ) { }
      }
    }
}

class Consumator extends Thread {
  private Buffer buffer ;

    public Consumator ( Buffer b ) {
      buffer = b ;
364                                   CAPITOLUL 12. FIRE DE EXECUTIE
                                                                 ¸

    }

    public void run () {
      int value = 0;
      for ( int i = 0; i < 10; i ++) {
        value = buffer . get () ;
        System . out . println ( " Consumatorul a primit : t " + value ) ;
      }
    }
}

public class TestSincron izare1 {
  public static void main ( String [] args ) {
    Buffer b = new Buffer () ;
    Producator p1 = new Producator ( b ) ;
    Consumator c1 = new Consumator ( b ) ;
    p1 . start () ;
    c1 . start () ;
  }
}

    Dup˘ cum ne a¸teptam, rezultatul rul˘rii acestui program nu va rezolva
        a          s                     a
nici pe departe problema propus˘ de noi, motivul fiind lipsa oric˘rei sin-
                                 a                                  a
croniz˘ri ˆ
      a ıntre cele dou˘ fire de executie. Mai precis, rezultatul va fi ceva de
                      a             ¸
forma:
    Consumatorul   a   primit:   -1
    Consumatorul   a   primit:   -1
    Producatorul   a   pus:      0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Consumatorul   a   primit:   0
    Producatorul   a   pus:      1
    Producatorul   a   pus:      2
    Producatorul   a   pus:      3
    Producatorul   a   pus:      4
    Producatorul   a   pus:      5
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                                 365

  Producatorul    a   pus:     6
  Producatorul    a   pus:     7
  Producatorul    a   pus:     8
  Producatorul    a   pus:     9

   Ambele fire de executie acceseaz˘ resursa comun˘, adic˘ obiectul de tip
                        ¸           a                 a   a
Buffer, ˆ
        ıntr-o manier˘ haotic˘ ¸i acest lucru se ˆ ampla din dou motive :
                     a       as                  ıntˆ

   • Consumatorul nu a¸teapt˘ ˆ
                       s     a ınainte de a citi ca produc˘torul s˘ genereze
                                                          a       a
     un num˘r ¸i va prelua de mai multe ori acela¸i num˘r.
           a s                                     s     a

   • Produc˘torul nu a¸teapt˘ consumatorul s˘ preia num˘rul generat ˆ
            a          s       a               a          a             ınainte
     de a produce un altul, ˆ felul acesta consumatorul va ”rata” cu sigurant˘
                            ın                                               ¸a
     unele numere (ˆ cazul nostru aproape pe toate).
                    ın

    Problema care se ridic˘ ˆ acest moment este: cine trebuie s˘ se ocupe de
                           a ın                                    a
sincronizarea celor dou˘ fire de executie : clasele Producator ¸i Consumator
                        a              ¸                         s
sau resursa comuna Buffer ? R˘spunsul este evident: resursa comun˘
                                     a                                         a
Buffer, deoarece ea trebuie s˘ permita sau nu accesul la continutul s˘u ¸i
                                a                                 ¸        a s
nu firele de executie care o folosesc. In felul acesta efortul sincroniz˘rii este
                   ¸                                                   a
transferat de la produc˘tor/consumator la un nivel mai jos, cel al resursei
                         a
critice.
    Activit˘¸ile produc˘torului ¸i consumatorului trebuie sincronizate la nivelul
           at          a        s
resursei comune ˆ dou˘ privinte:
                  ın    a        ¸

   • Cele dou˘ fire de executie nu trebuie s˘ acceseze simultan buffer-ul;
              a               ¸                 a
     acest lucru se realizeaz˘ prin blocarea obiectului Buffer atunci cˆnd
                             a                                            a
     este accesat de un fir de executie, astfel ˆ at nici nu alt fir de executie
                                    ¸          ıncˆ                        ¸
     s˘ nu-l mai poat˘ accesa (vezi ”Monitoare”).
      a               a

   • Cele dou˘ fire de executie trebuie s˘ se coordoneze, adic˘ produc˘torul
              a             ¸            a                    a        a
     trebuie s˘ g˘seasc˘ o modalitate de a ”spune” consumatorului c˘ a
              a a       a                                                 a
     plasat o valoare ˆ buffer, iar consumatorul trebuie s˘ comunice pro-
                      ın                                    a
     duc˘torului c˘ a preluat aceast˘ valoare, pentru ca acesta s˘ poat˘ gen-
         a        a                 a                            a     a
     era o alta. Pentru a realiza aceasta comunicare, clasa Thread pune la
     dispozitie metodele wait, notify, notifyAll. (vezi ”Semafoare”).
            ¸

   Folosind sincronizarea clasa Buffer va ar˘ta astfel:
                                            a
366                                  CAPITOLUL 12. FIRE DE EXECUTIE
                                                                ¸

                 Listing 12.8: Clasa Buffer cu sincronizare
class Buffer {
  private int number = -1;
  private boolean available = false ;

    public synchronized int get () {
      while (! available ) {
        try {
          wait () ;
          // Asteapta producatorul sa puna o valoare
        } catch ( Inter r u p t e d E x c e p t i o n e ) {
          e . printStackTrace () ;
        }
      }
      available = false ;
      notifyAll () ;
      return number ;
    }

    public synchronized void put ( int number ) {
      while ( available ) {
        try {
           wait () ;
           // Asteapta consumatorul sa preia valoarea
        } catch ( Inter r u p t e d E x c e p t i o n e ) {
           e . printStackTrace () ;
        }
      }
      this . number = number ;
      available = true ;
      notifyAll () ;
    }
}


    Rezultatul obtinut va fi cel scontat:

    Producatorul   a   pus:      0
    Consumatorul   a   primit:   0
    Producatorul   a   pus:      1
    Consumatorul   a   primit:   1
    ...
    Producatorul   a pus:    9
    Consumatorul   a primit: 9
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                                  367

12.3.6     Monitoare

Definitie¸
    Un segment de cod ce gestioneaz˘ o resurs˘ comun˘ mai multor de fire de
                                     a        a       a
executie separate concurente se nume¸te sectiune critic˘. In Java, o sectiune
      ¸                                s    ¸           a               ¸
critic˘ poate fi un bloc de instructiuni sau o metod˘.
      a                           ¸                a

    Controlul accesului ˆıntr-o sectiune critic˘ se face prin cuvˆntul cheie syn-
                                   ¸           a                 a
chronized. Platforma Java asociaz˘ un monitor (”lac˘t”) fiec˘rui obiect al
                                        a                   a        a
unui program aflat ˆ executie. Acest monitor va indica dac˘ resursa critic˘
                    ın        ¸                                  a              a
este accesat˘ de vreun fir de executie sau este liber˘, cu alte cuvinte ”mon-
            a                          ¸                 a
itorizeaz˘” resursa respectiv˘. In cazul ˆ care este accesat˘, va pune un
         a                     a             ın                    a
lac˘t pe aceasta, astfel ˆ at s˘ ˆ
   a                     ıncˆ a ımpiedice accesul altor fire de executie la ea.
                                                                         ¸
In momentul cˆnd resursa este eliberat˘ ”lac˘tul” va fi eliminat, pentru a
               a                           a      a
permite accesul altor fire de executie.¸
    In exemplul de tip produc˘tor/consumator de mai sus, sectiunile critice
                                 a                                  ¸
sunt metodele put ¸i get iar resursa critic˘ comun˘ este obiectul buffer.
                     s                          a         a
Consumatorul nu trebuie s˘ acceseze buffer-ul cˆnd producatorul tocmai
                              a                        a
pune o valoare ˆ el, iar produc˘torul nu trebuie s˘ modifice valoarea din
                ın                  a                     a
buffer ˆ momentul cˆnd aceasta este citit˘ de c˘tre consumator.
       ın              a                      a      a

  public synchronized int get() {
    ...
  }
  public synchronized void put(int number) {
    ...
  }

    S˘ observam c˘ ambele metode au fost declarate cu modificatorul synchronized.
      a           a
Cu toate acestea, sistemul asociaz˘ un monitor unei instante a clasei Buffer
                                     a                        ¸
¸i nu unei metode anume. In momentul ˆ care este apelat˘ o metod˘ sin-
s                                            ın                  a         a
cronizat˘, firul de executie care a facut apelul va bloca obiectul a c˘rei metod˘
         a               ¸                                           a          a
o acceseaz˘, ceea ce ˆ
           a          ınseamn˘ c˘ celelalte fire de executie nu vor mai putea
                               a a                         ¸
accesa resursele critice ale acelui obiect. Acesta este un lucru logic, deoarece
mai multe sectiuni critice ale unui obiect gestioneaz˘ de fapt o singur˘ resurs˘
              ¸                                       a                 a       a
critic˘.
      a
    In exemplul nostru, atunci cˆnd producatorul apeleaz˘ metoda put pen-
                                  a                          a
tru a scrie un num˘r, va bloca tot obiectul buffer, astfel c˘ firul de executie
                    a                                          a              ¸
368                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

consumator nu va avea acces la metoda get, ¸i reciproc.
                                           s
public synchronized void put(int number) {
  // buffer blocat de producator
  ...
  // buffer deblocat de producator
}
public synchronized int get() {
  // buffer blocat de consumator
  ...
  // buffer deblocat de consumator
}



    Monitoare fine
Adeseori, folosirea unui monitor pentru ˆıntreg obiectul poate fi prea restric-
tiv˘. De ce s˘ bloc˘m toate resursele unui obiect dac˘ un fir de executie
   a           a     a                                   a                 ¸
nu dore¸te decˆt accesarea uneia sau a cˆtorva dintre ele ? Deoarece orice
        s       a                         a
obiect are un monitor, putem folosi obiecte fictive ca lac˘te pentru fiecare
                                                           a
din resursele obiectului nostru, ca ˆ exemplul de mai jos:
                                    ın

class MonitoareFine {
  //Cele doua resurse ale obiectului
  Resursa x, y;

  //Folosim monitoarele a doua obiecte fictive
  Object xLacat = new Object(),
         yLacat = new Object();

  public void metoda() {
    synchronized(xLacat) {
      // Accesam resursa x
    }
    // Cod care nu foloseste resursele comune
    ...
    synchronized(yLacat) {
      // Accesam resursa y
    }
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                                 369

        ...
        synchronized(xLacat) {
          synchronized(yLacat) {
            // Accesam x si y
          }
        }
        ...
        synchronized(this) {
          // Accesam x si y
        }
    }
}

   Metoda de mai sus nu a fost declarat˘ cu synchronized ceea ce ar fi de-
                                            a
terminat blocarea tuturor resurselor comune la accesarea obiectului respectiv
de un fir de executie, ci au fost folosite monitoarele unor obiecte fictive pentru
                  ¸
a controla folosirea fiec˘rei resurs˘ ˆ parte.
                         a           a ın


12.3.7       Semafoare
Obiectul de tip Buffer din exemplul anterior are o variabil˘ membr˘ privat˘
                                                             a       a      a
numit˘ number, ˆ care este memorat num˘rul pe care ˆ comunic˘ produ-
      a           ın                         a            ıl         a
catorul ¸i pe care ˆ preia consumatorul. De asemenea, mai are o variabil˘
        s          ıl                                                       a
privat˘ logic˘ available care ne d˘ starea buffer-ului: dac˘ are valoarea true
      a      a                    a                       a
ˆ
ınseamn˘ c˘ produc˘torul a pus o valoare ˆ buffer ¸i consumatorul nu a
         a a          a                      ın         s
preluat-o ˆ
          ınca; dac˘ este false, consumatorul a preluat valoarea din buffer
                    a
dar produc˘torul nu a pus deocamdat˘ alta la loc. Deci, la prima vedere,
            a                           a
metodele clasei Buffer ar trebui s˘ arate astfel:
                                    a

    public synchronized int get() {
      while (!available) {
        // Nimic - asteptam ca variabila sa devina true
      }
      available = false;
      return number;
    }
    public synchronized int put(int number) {
      while (available) {
370                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

       // Nimic - asteptam ca variabila sa devina false
      }
      available = true;
      this.number = number;
  }
   Varianta de mai sus, de¸i pare corect˘, nu este. Aceasta deoarece im-
                            s             a
plementarea metodelor este ”selfish”, cele dou˘ metode ˆ asteapt˘ ˆ mod
                                              a          ısi        a ın
egoist conditia de terminare. Ca urmare, corectitudinea function˘rii va de-
            ¸                                                  ¸ a
pinde de sistemul de operare pe care programul este rulat, ceea ce reprezint˘
                                                                            a
o gre¸eal˘ de programare.
     s a
   Punerea corect˘ a unui fir de executie ˆ asteptare se realizeaz˘ cu metoda
                  a                   ¸ ın                       a
wait a clasei Thread, care are urm˘toarele forme:
                                   a
  void wait( )
  void wait( long timeout )
  void wait( long timeout, long nanos )
   Dup˘ apelul metodei wait, firul de executie curent elibereaz˘ monitorul
        a                                      ¸                 a
asociat obiectului respectiv ¸i a¸teapt˘ ca una din urm˘toarele conditii s˘ fie
                             s s       a               a             ¸ a
ˆ
ındeplinit˘:
          a
   • Un alt fir de executie informeaz˘ pe cei care ”a¸teapt˘” la un anumit
                       ¸             a                s    a
     monitor s˘ se ”trezeasc˘” - acest lucru se realizeaz˘ printr-un apel al
              a             a                            a
     metodei notifyAll sau notify.

   • Perioada de a¸tepatare specificat˘ a expirat.
                  s                  a
    Metoda wait poate produce exceptii de tipul InterruptedException,
                                           ¸
atunci cˆnd firul de executie care a¸teapt˘ (este deci ˆ starea ”Not Runnable”)
         a                ¸            s    a           ın
este ˆ
     ıntrerupt din a¸teptare ¸i trecut fortat ˆ starea ”Runnable”, de¸i conditia
                    s        s            ¸ ın                          s     ¸
a¸teptat˘ nu era ˆ a ˆ
 s       a        ınc˘ ındeplinit˘. a
    Metoda notifyAll informeaz˘ toate firele de executie care sunt ˆ asteptare
                                      a                    ¸            ın
la monitorul obiectului curent ˆ ındeplinirea conditiei pe care o a¸teptau. Metoda
                                                   ¸               s
notify informeaz˘ doar un singur fir de executie, specificat ca argument.
                   a                              ¸
    Reamintim varianta corect˘ a clasei Buffer:
                                  a

                     Listing 12.9: Folosirea semafoarelor
class Buffer {
  private int number = -1;
¸˘
12.3. CICLUL DE VIATA AL UNUI FIR DE EXECUTIE
                                          ¸                               371

    private boolean available = false ;

    public synchronized int get () {
      while (! available ) {
        try {
          wait () ;
          // Asteapta producatorul sa puna o valoare
        } catch ( InterruptedE x ce pt io n e ) {
          e . printStackTrace () ;
        }
      }
      available = false ;
      notifyAll () ;
      return number ;
    }

    public synchronized void put ( int number ) {
      while ( available ) {
        try {
           wait () ;
           // Asteapta consumatorul sa preia valoarea
        } catch ( InterruptedE x c e p t i o n e ) {
           e . printStackTrace () ;
        }
      }
      this . number = number ;
      available = true ;
      notifyAll () ;
    }
}




12.3.8     Probleme legate de sincronizare
Din p˘cate, folosirea monitoarelor ridic˘ ¸i unele probleme. S˘ analiz˘m
      a                                       a s                  a       a
cˆteva dintre ele ¸i posibilele lor solutii:
 a                s                     ¸
   Deadlock
Deadlock-ul este o problem˘ clasic˘ ˆ
                             a       a ıntr-un mediu ˆ care ruleaz˘ mai multe
                                                     ın           a
fire de executie ¸i const˘ ˆ faptul c˘, la un moment dat, ˆ
             ¸ s          a ın           a                     ıntreg procesul
se poate bloca deoarece unele fire a¸teapt˘ deblocarea unor monitoare care
                                       s       a
nu se vor debloca niciodat˘. Exist˘ numeroase exemple ˆ acest sens, cea
                             a         a                    ın
mai cunoscut˘ fiind ”Problema filozofilor”. Reformulat˘, s˘ ne imagin˘m
              a                                            a a             a
dou˘ persoane ”A” ¸i ”B” (fire de executie) care stau la aceea¸i mas˘ ¸i tre-
    a                s                       ¸                  s     as
372                                 CAPITOLUL 12. FIRE DE EXECUTIE
                                                               ¸

buie s˘ foloseasc˘ ˆ comun cutitul ¸i furculita (resursele comune) pentru a
       a          a ın            ¸     s        ¸
mˆnca. Evident, cele dou˘ persoane doresc obtinerea ambelor resurse. S˘
  a                          a                      ¸                           a
presupunem c˘ ”A” a otinut cutitul ¸i ”B” furculita. Firul ”A” se va bloca ˆ
               a         ¸        ¸    s              ¸                        ın
a¸teptarea eliber˘rii furculitei iar firul ”A” se va bloca ˆ a¸tepatrea eliber˘rii
 s                a          ¸                            ın s               a
cutitului, ceea ce conduce la starea de ”deadlock”. De¸i acest exemplu este
   ¸                                                       s
desprins de realitate, exist˘ numeroase situatii ˆ care fenomenul de ”dead-
                             a                  ¸ ın
lock” se poate manifesta, multe dintre acestea fiind dificil de detectat.
     Exist˘ cˆteva reguli ce pot fi aplicate pentru evitarea deadlock-ului:
          a a

   • Firele de executie s˘ solicite resursele ˆ aceea¸i ordine. Aceast˘ abor-
                    ¸ a                       ın     s                a
     dare elimin˘ situatiile de a¸teptare circular˘.
                 a     ¸          s               a

   • Folosirea unor monitoare care s˘ controleze accesul la un grup de resurse.
                                     a
     In cazul nostru, putem folosi un monitor ”tacˆmuri” care trebuie blocat
                                                   a
     ˆ
     ınainte de a cere furculita sau cutitul.
                              ¸        ¸

   • Folosirea unor variabile care s˘ informeze disponibilitatea resurselor
                                    a
     f˘r˘ a bloca monitoarele asociate acestora.
      aa

   • Cel mai importat, conceperea unei arhitecturi a sistemului care s˘ evite
                                                                      a
     pe cˆt posibil aparitia unor potentiale situatii de deaslock.
         a               ¸             ¸          ¸



    Variabile volatile
Cuvˆntul cheie volatile a fost introdus pentru a controla unele aspecte
    a
legate de optimiz˘rile efectuate de unele compilatoare. S˘ consider˘m urm˘torul
                 a                                       a         a     a
exemplu:

class TestVolatile {
  boolean test;
  public void metoda() {
    test = false;
    // *
    if (test) {
    // Aici se poate ajunge...
  }
}
}
12.4. GRUPAREA FIRELOR DE EXECUTIE
                               ¸                                         373

    Un compilator care optimizeaz˘ codul, poate decide c˘ variabila test fi-
                                     a                        a
ind setat˘ pe false, corpul if -ului nu se va executa ¸i s˘ exclud˘ secventa
          a                                               s a         a   ¸
respectiv˘ din rezultatul compil˘rii. Dac˘ aceast˘ clas˘ ar fi ˆ a accesat˘
          a                        a        a        a     a       ıns˘    a
de mai multe fire de executie, variabile test ar putea fi setat˘ pe true de un
                            ¸                                   a
alt fir, exact ˆ
              ıntre instructiunile de atribuire ¸i if ale firului curent.
                           ¸                    s
    Declararea unei variabile cu modificatorul volatile informeaz˘ compila-
                                                                      a
torul s˘ nu optimizeze codul ˆ care aceasta apare, previzionˆnd valoarea pe
       a                       ın                               a
care variabila o are la un moment dat.


    Fire de executie inaccesibile
                    ¸
Uneori firele de executie sunt blocate din alte motive decˆt a¸teptarea la
                       ¸                                     a s
un monitor, cea mai frecvent˘ situatie de acest tip fiind operatiunile de in-
                              a      ¸                         ¸
trare/ie¸ire (IO) blocante. Cˆnd acest lucru se ˆ ampl˘ celelalte fire de
        s                      a                   ınt˘   a
executie trebuie s˘ poat˘ accesa ˆ continuare obiectul. Dar dac˘ operatiunea
      ¸           a     a        ın                            a      ¸
IO a fost f˘cut˘ ˆ
           a a ıntr-o metod˘ sincronizat˘, acest lucru nu mai este posibil,
                              a           a
monitorul obiectului fiind blocat de firul care a¸teapt˘ de fapt s˘ realizeze
                                                 s      a         a
operatia de intrare/ie¸ire. Din acest motiv, operatiile IO nu trebuie f˘cute
      ¸               s                            ¸                   a
ˆ metode sincronizate.
ın


12.4      Gruparea firelor de executie
                                  ¸
Gruparea firelor de executie pune la dispozitie un mecanism pentru manipu-
                            ¸                 ¸
larea acestora ca un tot ¸i nu individual. De exemplu, putem s˘ pornim sau s˘
                         s                                    a             a
suspend˘m toate firele dintr-un grup cu un singur apel de metod˘. Gruparea
         a                                                       a
firelor de executie se realizeaz˘ prin intermediul clasei ThreadGroup.
                ¸                a
    Fiecare fir de executie Java este membru al unui grup, indiferent dac˘
                         ¸                                                  a
specific˘m explicit sau nu acest lucru. Afilierea unui fir la un anumit grup
        a
se realizeaz˘ la crearea sa ¸i devine permanent˘, ˆ sensul c˘ nu vom putea
            a                 s                  a ın        a
muta un fir dintr-un grup ˆ altul, dup˘ ce acesta a fost creat. In cazul ˆ
                              ın           a                               ın
care cre˘m un fir folosind un constructor care nu specific˘ din ce grup face
         a                                                 a
parte, el va fi plasat automat ˆ acela¸i grup cu firul de executie care l-a
                                   ın      s                      ¸
creat. La pornirea unui program Java se creeaz˘ automat un obiect de tip
                                                  a
ThreadGroup cu numele main, care va reprezenta grupul tuturor firelor de
executie create direct din program ¸i care nu au fost ata¸ate explicit altui
      ¸                                s                   s
grup. Cu alte cuvinte, putem s˘ ignor˘m complet plasarea firelor de executie
                                  a      a                                ¸
ˆ grupuri ¸i s˘ l˘s˘m sistemul s˘ se ocupe cu aceasta, adunˆndu-le pe toate
ın         s a aa                   a                        a
374                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

ˆ grupul main.
ın
   Exist˘ situatii ˆ a cˆnd gruparea firelor de executie poate u¸ura substantial
         a     ¸ ıns˘ a                              ¸           s           ¸
manevrarea lor. Crearea unui fir de executie ¸i plasarea lui ˆ
                                          ¸ s               ıntr-un grup (altul
decˆt cel implicit) se realizeaz˘ prin urm˘torii constructori ai clasei Thread:
   a                            a         a
  public Thread(ThreadGroup group, Runnable target)
  public Thread(ThreadGroup group, String name)
  public Thread(ThreadGroup group, Runnable target, String name)
Fiecare din ace¸ti costructori creeaz˘ un fir de executie, ˆ initializeaz˘ ¸i
                 s                    a              ¸ ıl      ¸        a s
ˆ plaseaz˘ ˆ
ıl        a ıntr-un grup specificat ca argument. Pentru a afla c˘rui grup
                                                                  a
apartine un anumit fir de executie putem folosi metoda getThreadGroup a
     ¸                           ¸
clasei Thread. In exemplul urm˘tor vor fi create dou˘ grupuri, primul cu
                                  a                  a
dou˘ fire de executie iar al doilea cu trei:
    a              ¸
  ThreadGroup grup1 = new ThreadGroup("Producatori");
  Thread p1 = new Thread(grup1, "Producator 1");
  Thread p2 = new Thread(grup1, "Producator 2");

  ThreadGroup    grup2 = new ThreadGroup("Consumatori");
  Thread c1 =    new Thread(grup2, "Consumator 1");
  Thread c2 =    new Thread(grup2, "Consumator 2");
  Thread c3 =    new Thread(grup2, "Consumator 3");
   Un grup poate avea ca p˘rinte un alt grup, ceea ce ˆ
                             a                           ınseamn˘ c˘ firele de
                                                                 a a
executie pot fi plasate ˆ
     ¸                 ıntr-o ierarhie de grupuri, ˆ care r˘d˘cina este grupul
                                                   ın      a a
implicit main, ca ˆ figura de mai jos:
                  ın
12.4. GRUPAREA FIRELOR DE EXECUTIE
                               ¸                                       375

    S˘ consider˘m un exemplu ˆ care list˘m firele de executie active:
     a         a             ım         a                ¸




                   Listing 12.10: Folosirea clasei ThreadGroup

public class TestThreadGroup {

    static class Dummy implements Runnable {
      public void run () {
        while ( true )
          Thread . yield () ;
      }
    }

    public static void main ( String args []) {

        // Cream o fereastra pentru a fi create
        // automat firele de executie din AWT
        java . awt . Frame f = new java . awt . Frame ( " Test " ) ;

        // Cream un fir propriu
        new Thread ( new Dummy () , " Fir de test " ) . start () ;

        // Obtinem o referinta la grupul curent
        Thread firCurent = Thread . currentThread () ;
        ThreadGroup grupCurent = firCurent . getThreadGroup () ;

        // Aflam numarul firelor de executie active
        int n = grupCurent . activeCount () ;

        // Enumeram firele din grup
        Thread [] lista = new Thread [ n ];
        grupCurent . enumerate ( lista ) ;

        // Le afisam
        for ( int i =0; i < n ; i ++)
          System . out . println ( " Thread # " + i + " = " +
            lista [ i ]. getName () ) ;
    }
}
376                                 CAPITOLUL 12. FIRE DE EXECUTIE
                                                               ¸

12.5       Comunicarea prin fluxuri de tip ”pipe”
O modalitate deosebit de util˘ prin care dou˘ fire de executie pot comunica
                               a               a             ¸
este realizat˘ prin intermediul canalelor de comunicatii (pipes). Acestea sunt
             a
implementate prin fluxuri descrise de clasele:
   • PipedReader, PipedWriter - pentru caractere, respectiv

   • PipedOutputStream, PipedInputStream - pentru octeti.
                                                      ¸
    Fluxurile ”pipe” de ie¸ire ¸i cele de intrare pot fi conectate pentru a
                          s    s
efectua transmiterea datelor. Acest lucru se realizeaz˘ uzual prin intemediul
                                                      a
constructorilor:
  public PipedReader(PipedWriterpw)
  public PipedWriter(PipedReaderpr)
In cazul ˆ care este folosit un constructor f˘r˘ argumente, conectarea unui
         ın                                  aa
flux de intrare cu un flux de ie¸ire se face prin metoda connect:
                               s
  public void connect(PipedWriterpw)
  public void connect(PipedReaderpr)
    Intrucˆt fluxurile care sunt conectate printr-un pipe trebuie s˘ execute
           a                                                           a
simultan operatii de scriere/citire, folosirea lor se va face din cadrul unor fire
                ¸
de executie.
         ¸
    Functionarea obicetelor care instantiaz˘ PipedWriter ¸i PipedReader
         ¸                                 ¸ a                   s
este asem˘n˘toare cu a canalelor de comunicare UNIX (pipes). Fiecare cap˘t
           a a                                                                 a
al unui canal este utilizat dintr-un fir de executie separat. La un cap˘t se
                                                    ¸                       a
scriu caractere, la cel˘lalt se citesc. La citire, dac˘ nu sunt date disponibile
                       a                              a
firul de executie se va bloca pˆn˘ ce acestea vor deveni disponibile. Se observ˘
              ¸                a a                                              a
c˘ acesta este un comportament tipic produc˘tor-consumator asincron, firele
 a                                               a
de executie comunicˆnd printr-un canal.
         ¸           a
    Realizarea conexiunii se face astfel:
  PipedWriter    pw1 = new PipedWriter();
  PipedReader    pr1 = new PipedReader(pw1);
  // sau
  PipedReader    pr2 = new PipedReader();
  PipedWriter    pw2 = new PipedWriter(pr2);
  // sau
12.5. COMUNICAREA PRIN FLUXURI DE TIP ”PIPE”                              377

    PipedReader pr = new PipedReader();
    PipedWriter pw = new PipedWirter();
    pr.connect(pw) //echivalent cu
    pw.connect(pr);
Scrierea ¸i citirea pe/de pe canale se realizeaz˘ prin metodele uzuale read ¸i
          s                                      a                           s
write, ˆ toate formele lor.
         ın
    S˘ reconsider˘m acum exemplul produc˘tor/consumator prezentat ante-
      a            a                           a
rior, folosind canale de comunicatie. Produc˘torul trimite datele printr-un
                                   ¸             a
flux de ie¸ire de tip DataOutputStream c˘tre consumator, care le prime¸te
            s                                a                             s
printr-un flux de intrare de tip DataInputStream. Aceste dou˘ fluxuri vor fi
                                                                a
interconectate prin intermediul unor fluxuri de tip ”pipe”.

               Listing 12.11: Folosirea fluxurilor de tip ”pipe”
import java . io .*;

class Producator extends Thread {
  private DataOutputStream out ;

    public Producator ( DataOutputStream out ) {
      this . out = out ;
    }

    public void run () {
      for ( int i = 0; i < 10; i ++) {
        try {
          out . writeInt ( i ) ;
        } catch ( IOException e ) {
          e . printStackTrace () ;
        }
        System . out . println ( " Producatorul a pus : t " + i ) ;
        try {
          sleep (( int ) ( Math . random () * 100) ) ;
        } catch ( InterruptedE x c e p t i o n e ) { }
      }
    }
}

class Consumator extends Thread {
  private DataInputStream in ;

    public Consumator ( DataInputStream in ) {
      this . in = in ;
378                                CAPITOLUL 12. FIRE DE EXECUTIE
                                                              ¸

    }

    public void run () {
      int value = 0;
      for ( int i = 0; i < 10; i ++) {
        try {
          value = in . readInt () ;
        } catch ( IOException e ) {
          e . printStackTrace () ;
        }
        System . out . println ( " Consumatorul a primit : t " + value ) ;
      }
    }
}


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

        PipedOutputStream pipeOut = new Pipe dOutput Stream () ;
        PipedInputStream pipeIn = new PipedInputStream ( pipeOut ) ;

        DataOutputStream out = new DataOutputStream ( pipeOut ) ;
        DataInputStream in = new DataInputStream ( pipeIn ) ;

        Producator p1 = new Producator ( out ) ;
        Consumator c1 = new Consumator ( in ) ;

        p1 . start () ;
        c1 . start () ;
    }
}




12.6          Clasele Timer ¸i TimerTask
                            s
Clasa Timer ofer˘ o facilitate de a planifica diverse actiuni pentru a fi real-
                  a                                       ¸
izate la un anumit moment de c˘tre un fir de executie ce ruleaz˘ ˆ fundal.
                                  a                    ¸         a ın
Actiunile unui obiect de tip Timer sunt implementate ca instante ale clasei
   ¸                                                             ¸
TimerTask ¸i pot fi programate pentru o singur˘ executie sau pentru executii
            s                                    a       ¸                ¸
repetate la intervale regulate.
    Pa¸ii care trebuie f˘cuti pentru folosirea unui timer sunt:
       s                a ¸
12.6. CLASELE TIMER SI TIMERTASK
                    ¸                                                     379

   • Crearea unei subclase Actiune a lui TimerTask ¸i supreadefinirea metodei
                                                   s
     run ce va contine actiunea pe care vrem s˘ o planific˘m. Dup˘ cum
                   ¸      ¸                      a         a        a
     vom vedea, pot fi folosite ¸i clase anonime.
                               s

   • Crearea unui fir de executie prin instantierea clasei Timer;
                             ¸              ¸

   • Crearea unui obiect de tip Actiune;

   • Planificarea la executie a obiectuluii de tip Actiune, folosind metoda
                         ¸
     schedule din clasa Timer;

  Metodele de planificare pe care le avem la dispozitie au urm˘toarele for-
                                                   ¸         a
mate:

  schedule(TimerTask task, Date        time)
  schedule(TimerTask task, long        delay, long period)
  schedule(TimerTask task, Date        time, long period)
  scheduleAtFixedRate(TimerTask        task, long delay, long period)
  scheduleAtFixedRate(TimerTask        task, Date time, long period)

unde, task descrie actiunea ce se va executa, delay reprezint˘ ˆ arzierea fata
                     ¸                                        a ıntˆ        ¸
de momentul curent dup˘ care va ˆ
                          a         ıncepe executia, time momentul exact la
                                                 ¸
care va ˆ
        ıncepe executia iar period intervalul de timp ˆ
                     ¸                                 ıntre dou˘ executii.
                                                                 a     ¸
   Dup˘ cum se observ˘, metodele de planificare se ˆ
        a               a                            ımpart ˆ dou˘ categorii:
                                                             ın      a

   • schedule - planificare cu ˆ arziere fix˘: dac˘ dintr-un anumit motiv
                               ıntˆ        a      a
     actiunea este ˆ arziat˘, urm˘toarele actiuni vor fi ¸i ele ˆ arziate ˆ
       ¸           ıntˆ    a      a         ¸           s      ıntˆ      ın
     consecint˘;
             ¸a

   • scheduleAtFixedRate - planificare cu num˘r fix de rate: dac˘ dintr-
                                                 a                 a
     un anumit motiv actiunea este ˆ arziat˘, urm˘toarele actiuni vor fi
                         ¸            ıntˆ     a    a          ¸
     executat˘ mai repede, astfel ˆ at num˘rul total de actiuni dintr-o pe-
             a                    ıncˆ       a            ¸
     rioad˘ de timp s˘ fie tot timpul acela¸i;
          a          a                     s

    Un timer se va opri natural la terminarea metodei sale run sau poate fi
oprit fortat folosind metoda cancel. Dup˘ oprirea sa el nu va mai putea fi
         ¸                                  a
folosit pentru planificarea altor actiuni. De asemenea, metoda System.exit
                                   ¸
va oprit fortat toate firele de executie ¸i va termina aplicatia curent˘.
            ¸                        ¸ s                    ¸         a
380                                 CAPITOLUL 12. FIRE DE EXECUTIE
                                                               ¸

             Listing 12.12: Folosirea claselor Timer ¸i TimerTask
                                                     s
import java . util .*;
import java . awt .*;

class Atentie extends TimerTask {
  public void run () {
    Toolkit . getDefaultToolkit () . beep () ;
    System . out . print ( " . " ) ;
  }
}

class Alarma extends TimerTask {
  public String mesaj ;
  public Alarma ( String mesaj ) {
    this . mesaj = mesaj ;
  }
  public void run () {
    System . out . println ( mesaj ) ;
  }
}

public class TestTimer {
  public static void main ( String args []) {

      // Setam o actiune repetitiva , cu rata fixa
      final Timer t1 = new Timer () ;
      t1 . scheduleAtFix e d Ra t e ( new Atentie () , 0 , 1*1000) ;

      // Folosim o clasa anonima pentru o alta actiune
      Timer t2 = new Timer () ;
      t2 . schedule ( new TimerTask () {
         public void run () {
            System . out . println ( "S - au scurs 10 secunde . " ) ;
            // Oprim primul timer
            t1 . cancel () ;
         }
      } , 10*1000) ;

      // Setam o actiune pentru ora 22:30
      Calendar calendar = Calendar . getInstance () ;
      calendar . set ( Calendar . HOUR_OF_DAY , 22) ;
      calendar . set ( Calendar . MINUTE , 30) ;
      calendar . set ( Calendar . SECOND , 0) ;
      Date ora = calendar . getTime () ;
12.6. CLASELE TIMER SI TIMERTASK
                    ¸                                                      381

        Timer t3 = new Timer () ;
        t3 . schedule ( new Alarma ( " Toti copiii la culcare ! " ) , ora ) ;
    }
}
382   CAPITOLUL 12. FIRE DE EXECUTIE
                                 ¸
Capitolul 13

Programare ˆ retea
           ın ¸

13.1       Introducere
Programarea ˆ retea implic˘ trimiterea de mesaje ¸i date ˆ
               ın ¸           a                        s        ıntre aplicatii ce
                                                                            ¸
ruleaz˘ pe calculatoare aflate ˆ
       a                        ıntr-o retea local˘ sau conectate la Internet.
                                         ¸        a
Pachetul care ofer˘ suport pentru scrierea aplicatiilor de retea este java.net.
                    a                                         ¸
Clasele din acest pachet ofer˘ o modalitate facil˘ de programare ˆ retea, f˘r˘
                             a                   a                  ın ¸       aa
a fi nevoie de cuno¸tinte prealabile referitoare la comunicarea efectiv˘ ˆ
                      s ¸                                                  a ıntre
calculatoare. Cu toate acestea, sunt necesare cˆteva notiuni fundamentale
                                                   a        ¸
referitoare la retele cum ar fi: protocol, adresa IP, port, socket.
                 ¸


   Ce este un protocol ?
Un protocol reprezint˘ o conventie de reprezentare a datelor folosit˘ ˆ comu-
                      a           ¸                                     a ın
nicarea ˆıntre dou˘ calculatoare. Avˆnd ˆ vedere faptul c˘ orice informatie
                  a                   a ın                      a             ¸
care trebuie trimis˘ prin retea trebuie serializat˘ astfel ˆ at s˘ poat˘ fi trans-
                   a         ¸                     a       ıncˆ a        a
mis˘ secvential, octet cu octet, c˘tre destinatie, era nevoie de stabilirea unor
   a         ¸                      a           ¸
conventii (protocoale) care s˘ fie folosite atˆt de calculatorul care trimite
       ¸                        a                a
datele cˆt ¸i de cel care le prime¸te, pentru a se ”ˆ ¸elege” ˆ
         a s                        s                ınt         ıntre ele.
   Dou˘ dintre cele mai utilizate protocoale sunt TCP ¸i UDP.
        a                                                     s
   • TCP (Transport Control Protocol) este un protocol ce furnizeaz˘ un  a
     flux sigur de date ˆ ıntre dou˘ calculatoare aflate ˆ retea. Acest proto-
                                  a                    ın ¸
     col asigur˘ stabilirea unei conexiuni permanente ˆ
               a                                        ıntre cele dou˘ calcu-
                                                                      a
     latoare pe parcursul comunicatiei.
                                     ¸
   • UDP (User Datagram Protocol) este un protocol bazat pe pachete inde-

                                       383
384                          CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                      IN  ¸

      pendente de date, numite datagrame, trimise de la un calculator c˘tre
                                                                          a
      altul f˘r˘ a se garanta ˆ vreun fel ajungerea acestora la destinatie sau
             aa                ın                                      ¸
      ordinea ˆ care acestea ajung. Acest protocol nu stabile¸te o conexiun˘
               ın                                             s              a
      permant˘ ˆa ıntre cele dou˘ calculatoare.
                                a



   Cum este identificat un calculator ˆ retea ?ın ¸
Orice calculator conectat la Internet este identificat ˆ mod unic de adresa sa
                                                       ın
IP (IP este acronimul de la Internet Protocol). Aceasta reprezint˘ un num˘r
                                                                    a       a
reprezentat pe 32 de biti, uzual sub forma a 4 octeti, cum ar fi de exemplu:
                         ¸                            ¸
193.231.30.131 ¸i este numit adresa IP numeric˘. Corespunz˘toare unei
                   s                                 a              a
adrese numerice exista ¸i o adresa IP simbolic˘, cum ar fi thor.infoiasi.ro
                        s                      a
pentru adresa numeric˘ anterioar˘.
                        a          a
   De asemenea, fiecare calculator aflat ˆ  ıntr-o retea local˘ are un nume unic
                                                   ¸        a
ce poat fi folosit la identificarea local˘ a acestuia.
                                       a
   Clasa Java care reprezint˘ notiunea de adres˘ IP este InetAddress.
                              a ¸                  a


    Ce este un port ?
Un calculator are ˆ general o singur˘ leg˘tur˘ fizic˘ la retea. Orice informatie
                  ın                 a a a         a       ¸                    ¸
destinat˘ unei anumite ma¸ini trebuie deci s˘ specifice obligatoriu adresa
         a                   s                  a
IP a acelei ma¸ini. Ins˘ pe un calculator pot exista concurent mai multe
                s       a
procese care au stabilite conexiuni ˆ retea, asteptˆnd diverse informatii.
                                       ın ¸            a                        ¸
Prin urmare, datele trimise c˘tre o destinatie trebuie s˘ specifice pe lˆnga
                               a              ¸             a                a
adresa IP a calculatorului ¸i procesul c˘tre care se ˆ
                             s            a              ındreapt˘ informatiile
                                                                   a          ¸
respective. Identificarea proceselor se realizeaz˘ prin intermdiul porturilor.
                                                a
    Un port este un num˘r pe 16 biti care identific˘ ˆ mod unic procesele
                          a           ¸               a ın
care ruleaz˘ pe o anumit˘ masin˘. Orice aplicatie care realizeaz˘ o conexiune
           a            a        a              ¸                   a
ˆ retea va trebui s˘ ata¸eze un num˘r de port acelei conexiuni. Valorile pe
ın ¸                a    s             a
care le poate lua un num˘r de port sunt cuprinse ˆ
                           a                        ıntre 0 ¸i 65535 (deoarece
                                                              s
sunt numere reprezentate pe 16 biti), numerele cuprinse ˆ
                                   ¸                         ıntre 0 ¸i 1023 fiind
                                                                      s
ˆ a rezervate unor servicii sistem ¸i, din acest motiv, nu trebuie folosite ˆ
ıns˘                                s                                            ın
aplicatii.
      ¸


   Clase de baz˘ din java.net
                 a
Clasele din java.net permit comunicarea ˆ
                                        ıntre procese folosind protocoalele
13.2. LUCRUL CU URL-URI                                                    385

TCP ¸i UDP ¸i sunt prezentate ˆ tabelul de mai jos.
    s      s                  ın

                    TCP                UDP
                    URL                DatagramPacket
                    URLConnection      DatagramSocket
                    Socket             MulticastSocket
                    ServerSocket


13.2      Lucrul cu URL-uri
Termenul URL este acronimul pentru Uniform Resource Locator ¸i reprezint˘
                                                                   s         a
o referint˘ (adres˘) la o resurs˘ aflat˘ pe Internet. Aceasta este ˆ general un
          ¸a       a            a     a                           ın
fi¸ier reprezentˆnd o pagin˘ Web, un text, imagine, etc., ˆ a un URL poate
 s              a            a                             ıns˘
referi ¸i interog˘ri la baze de date, rezultate ale unor comenzi executate la
       s         a
distanta, etc. Mai jost, sunt prezentate cˆteva exemple de URL-uri sunt:
       ¸˘                                  a

  http://java.sun.com
  http://students.infoiasi.ro/index.html
  http://www.infoiasi.ro/~acf/imgs/taz.gif
  http://www.infoiasi.ro/~acf/java/curs/9/prog_retea.html#url

   Dup˘ cum se observ˘ din exemplele de mai sus, un URL are dou˘ com-
       a             a                                         a
ponente principale:

   • Identificatorul protocolului folosit (http, ftp, etc);

   • Numele resursei referite. Acesta are urm˘toarele componente:
                                             a

        – Numele calculatorului gazd˘ (www.infoiasi.ro).
                                    a
        – Calea complet˘ spre resursa referit˘ ( acf/java/curs/9/prog retea.html).
                        a                    a
          Notatia user semnific˘ uzual subdirectorul html al directoru-
                ¸                 a
          lui rezervat pe un server Web utilizatorului specificat (HOME). In
          cazul ˆ care este specificat doar un director, fi¸ierul ce reprezint˘
                 ın                                      s                  a
          resursa va fi considerat implicit index.html.
        – Optional, o referint˘ de tip anchor ˆ cadrul fi¸ierului referit (#url).
            ¸                ¸a               ın        s
        – Optional, portul la care s˘ se realizeze conexiunea.
            ¸                       a
386                         CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                     IN  ¸

   Clasa care permite lucrul cu URL-uri este java.net.URL. Aceasta are
mai multi constructori pentru crearea de obiecte ce reprezint˘ referinte c˘tre
        ¸                                                    a        ¸ a
resurse aflate ˆ retea, cel mai uzual fiind cel care prime¸te ca parametru
              ın ¸                                         s
un ¸ir de caractere. In cazul ˆ care ¸irul nu reprezint˘ un URL valid va fi
   s                          ın     s                 a
aruncat˘ o exceptie de tipul MalformedURLException.
        a        ¸

  try {
    URL adresa = new URL("http://xyz.abc");
  } catch (MalformedURLException e) {
    System.err.println("URL invalid !n" + e);
  }

   Un obiect de tip URL poate fi folosit pentru:

   • Aflarea informatiilor despre resursa referit˘ (numele calculatorului gazd˘,
                    ¸                            a                           a
     numele fi¸ierului, protocolul folosit. etc).
              s

   • Citirea printr-un flux a continutului fi¸ierului respectiv.
                                           s

   • Conectarea la acel URL pentru citirea ¸i scrierea de informatii.
                                           s                     ¸



     Citirea continutului unui URL
                 ¸
Orice obiect de tip URL poate returna un flux de intrare de tip InputStream
pentru citirea continutului s˘u. Secventa standard pentru aceast˘ operatiune
                             a         ¸                        a
este prezentat˘ ˆ exemplul de mai jos, ˆ care afi¸am continutul resursei
               a ın                        ın        s       ¸
specificat˘ la linia de comand˘. Dac˘ nu se specific˘ mici un argument, va fi
          a                    a     a             a
afi¸at fi¸ierul index.html de la adresa: http://www.infoiasi.ro.
   s    s

                Listing 13.1: Citirea continutului unui URL
                                         ¸
import java . net .*;
import java . io .*;

public class CitireURL {
  public static void main ( String [] args )
      throws IOException {
    String adresa = " http :// www . infoiasi . ro " ;
    if ( args . length > 0)
      adresa = args [0];
13.3. SOCKET-URI                                                           387

        BufferedReader br = null ;
        try {
          URL url = new URL ( adresa ) ;
          InputStream in = url . openStream () ;
          br = new BufferedReader ( new Inpu tStream Reader ( in ) ) ;
          String linie ;
          while (( linie = br . readLine () ) != null ) {
             // Afisam linia citita
             System . out . println ( linie ) ;
          }
        } catch ( Malfo rmedU RLEx c e p t i o n e ) {
          System . err . println ( " URL invalid ! n " + e ) ;
        } finally {
          br . close () ;
        }
    }
}




    Conectarea la un URL
Se realizeaz˘ prin metoda openConnection ce stabile¸te o conexiune bidirectional˘
              a                                          s                      ¸ a
cu resursa specificat˘. Aceast˘ conexiune este reprezentat˘ de un obiect de
                      a         a                             a
tip URLConnection, ce permite crearea atˆt a unui flux de intrare pentru
                                               a
citirea informatiilor de la URL-ul specificat, cˆt ¸i a unui flux de ie¸ire pentru
                ¸                              a s                   s
scrierea de date c˘tre acel URL. Operatiunea de trimitere de date dintr-un
                    a                     ¸
program c˘tre un URL este similar˘ cu trimiterea de date dintr-un formular
            a                        a
de tip FORM aflat ˆ ıntr-o pagin˘ HTML. Metoda folosit˘ pentru trimitere este
                               a                         a
POST.
    In cazul trimiterii de date, obiectul URL este uzual un proces ce ruleaz˘  a
pe serverul Web referit prin URL-ul respectiv (jsp, servlet, cgi-bin, php, etc).


13.3         Socket-uri

Definitie¸
    Un socket (soclu) este o abstractiune software folosit˘ pentru a reprezenta
                                    ¸                     a
fiecare din cele dou˘ ”capete” ale unei conexiuni ˆ
                   a                              ıntre dou˘ procese ce ruleaz˘
                                                            a                 a
ˆ
ıntr-o retea. Fiecare socket este ata¸at unui port astfel ˆ at s˘ poat˘ iden-
          ¸                           s                    ıncˆ a       a
tifica unic programul c˘ruia ˆ sunt destinate datele.
                        a     ıi
388                         CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                     IN  ¸

   Socket-urile sunt de dou˘ tipuri:
                           a

   • TCP, implementate de clasele Socket ¸i ServerSocket;
                                         s

   • UDP, implementate de clasa DatagramSocket.

    O aplicatie de retea ce folose¸te socket-uri se ˆ
             ¸       ¸            s                 ıncadreaz˘ ˆ modelul clien-
                                                             a ın
t/server de concepere a unei aplicatii. In acest model aplicatia este format˘
                                       ¸                         ¸             a
din dou˘ categorii distincte de programe numite servere, respectiv clienti.
         a                                                                 ¸
    Programele de tip server sunt cele care ofer˘ diverse servicii eventualilor
                                                   a
clienti, fiind ˆ stare de a¸teptare atˆta vreme cˆt nici un client nu le solicit˘
     ¸         ın          s           a           a                           a
serviciile. Programele de tip client sunt cele care initiaz˘ conversatia cu un
                                                         ¸ a          ¸
server, solicitˆnd un anumit serviciu. Uzual, un server trebuie s˘ fie capabil s˘
                a                                                  a           a
trateze mai multi clienti simultan ¸i, din acest motiv, fiecare cerere adresat˘
                  ¸                  s                                         a
serverului va fi tratat˘ ˆ
                       a ıntr-un fir de executie separat.
                                              ¸


    Incepˆnd cu versiunea 1.4 a platformei standard Java, exist˘ o clas˘ utili-
          a                                                     a      a
tar˘ care implementeaz˘ o pereche de tipul (adresa IP, num˘r port). Aceasta
   a                    a                                   a
este InetSocketAddress (derivat˘ din SocketAddress), obiectele sale fiind
                                   a
utilizate de constructori ¸i metode definite ˆ cadrul claselor ce descriu sock-
                          s                 ın
eturi, pentru a specifica cei doi parametri necesari identific˘rii unui proces
                                                              a
care trimite sau receptioneaz˘ date ˆ retea.
                      ¸       a      ın ¸


13.4       Comunicarea prin conexiuni
In acest model se stabile¸te o conexiune TCP ˆ
                            s                      ıntre o aplicatie client ¸i o
                                                                 ¸          s
aplicatie server care furnizeaz˘ un anumit serviciu. Avantajul protocolul
      ¸                           a
TCP/IP este c˘ asigur˘ realizarea unei comunic˘ri stabile, permanente ˆ
                  a      a                           a                        ın
retea, existˆnd siguranta c˘ informatiile trimise de un proces vor fi receptionate
  ¸         a           ¸ a          ¸                                    ¸
corect ¸i complet la destinatie sau va fi semnalat˘ o exceptie ˆ caz contrar.
        s                     ¸                     a        ¸ ın
    Leg˘tura ˆ
        a       ıntre un client ¸i un server se realizeaz˘ prin intermediul a
                                s                         a
dou˘ obiecte de tip Socket, cˆte unul pentru fiecare cap˘t al ”canalului”
    a                             a                           a
de comunicatie dintre cei doi. La nivelul clientului crearea socketului se re-
              ¸
alizeaz˘ specificˆnd adresa IP a serverului ¸i portul la care ruleaz˘ acesta,
       a           a                          s                        a
constructorul uzual folosit fiind:

  Socket(InetAddress address, int port)
13.4. COMUNICAREA PRIN CONEXIUNI                                           389

    La nivelul serverului, acesta trebuie s˘ creeze ˆ ai un obiect de tip
                                             a          ıntˆ
ServerSocket. Acest tip de socket nu asigur˘ comunicarea efectiv˘ cu
                                                    a                     a
clientii ci este responsabil cu ”ascultarea” retelei ¸i crearea unor obiecte de
     ¸                                         ¸     s
tip Socket pentru fiecare cerere ap˘rut˘, prin intermediul cˆruia va fi real-
                                     a a                        a
izat˘ leg˘tura cu clientul. Crearea unui obiect de tip ServerSocket se face
    a      a
specificˆnd portul la care ruleaz˘ serverul, constructorul folosit fiind:
         a                        a
  ServerSocket(int port)
Metoda clasei ServerSocket care a¸teapt˘ ”ascult˘” reteaua este accept.
                                    s     a         a    ¸
Aceasta blocheaz˘ procesul p˘rinte pˆn˘ la aparitia unui cereri ¸i returneaz˘
                 a           a       a a        ¸               s           a
un nou obiect de tip Socket ce va asigura comunicarea cu clientul. Blocarea
poate s˘ nu fie permanent˘ ci doar pentru o anumit˘ perioad˘ de timp -
        a                  a                          a          a
aceasta va fi specificat˘ prin metoda setSoTimeout, cu argumentul dat ˆ
                      a                                                    ın
milisecunde.




    Pentru fiecare din cele dou˘ socketuri deschise pot fi create apoi dou˘
                                a                                             a
fluxuri pe octeti pentru citirea, respectiv scrierea datelor. Acest lucru se re-
               ¸
alizeaz˘ prin intermediul metodelor getInputStream, respectuv getOut-
       a
putStream. Fluxurile obtinute vor fi folosite ˆ
                           ¸                     ımpreun˘ cu fluxuri de proce-
                                                         a
sare care s˘ asigure o comunicare facil˘ ˆ
           a                            a ıntre cele dou˘ procese. In functie
                                                         a                  ¸
de specificul aplicatiei acestea pot fi perechile:
                   ¸
   • BufferedReader, BufferedWriter ¸i PrintWriter - pentru comuni-
                                           s
     care prin intermediul ¸irurilor de caractere;
                           s
   • DataInputStream, DataOutputStream - pentru comunicare prin date
     primitive;
   • ObjectInputStream, ObjectOutputStream - pentru cominicare prin
     intermediul obiectelor;
390                        CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                    IN  ¸

   Structura general˘ a unui server bazat pe conexiuni este:
                    a

  1. Creeaza un obiect de tip ServerSocket la un anumit port
  while (true) {
    2. Asteapta realizarea unei conexiuni cu un client,
       folosind metoda accept;
       (va fi creat un obiect nou de tip Socket)
    3. Trateaza cererea venita de la client:
       3.1 Deschide un flux de intrare si primeste cererea
       3.2 Deschide un flux de iesire si trimite raspunsul
       3.3 Inchide fluxurile si socketul nou creat
  }

   Este recomandat ca tratarea cererilor s˘ se realizeze ˆ fire de executie
                                              a          ın            ¸
separate, pentru ca metoda accept s˘ poat˘ fi reapelat˘ cˆt mai repede ˆ
                                       a        a       a a             ın
vederea stabilirii conexiunii cu un alt client.
   Structura general˘ a unui client bazat pe conexiuni este:
                      a

  1. Citeste sau declara adresa IP a serverului si portul
     la care acesta ruleaza;
  2. Creeaza un obiect de tip Socket cu adresa si portul specificate;
  3. Comunica cu serverul:
     3.1 Deschide un flux de iesire si trimite cererea;
     3.2 Deschide un flux de intrare si primeste raspunsul;
     3.3 Inchide fluxurile si socketul creat;

    In exemplul urm˘tor vom implementa o aplicatie client-server folosind
                     a                              ¸
comunicarea prin conexiuni. Clientul va trimite serverului un nume iar acesta
va raspunde prin mesajul ”Hello nume”. Tratarea cererilor se va face ˆ fire
                                                                       ın
de executie separate.
         ¸

          Listing 13.2: Structura unui server bazat pe conexiuni
import java . net .*;
import java . io .*;

class ClientThread extends Thread {
  Socket socket = null ;

  public ClientThread ( Socket socket ) {
    this . socket = socket ;
13.4. COMUNICAREA PRIN CONEXIUNI                                       391

    }

    public void run () {
      // Executam solicitarea clientului
      String cerere , raspuns ;
      try {
         // in este fluxul de intrare de la client
         BufferedReader in = new BufferedReader ( new
            InputStreamReader (
           socket . getInputStream () ) ) ;

          // out este flux de iesire catre client
          PrintWriter out = new PrintWriter (
            socket . getOutputStream () ) ;

          // Primim cerere de la client
          cerere = in . readLine () ;

          // Trimitem raspuns clientului
          raspuns = " Hello " + cerere + " ! " ;
          out . println ( raspuns ) ;
          out . flush () ;

        } catch ( IOException e ) {
          System . err . println ( " Eroare IO  n " + e ) ;
        } finally {
          // Inchidem socketul deschis pentru clientul curent
          try {
            socket . close () ;
          } catch ( IOException e ) {
            System . err . println ( " Socketul nu poate fi inchis  n " +
                 e);
          }
        }
    }
}

public class SimpleServer {

    // Definim portul pe care se gaseste serverul
    // ( in afara intervalului 1 -1024)
    public static final int PORT = 8100;

    public SimpleServer () throws IOException {
      ServerSocket serverSocket = null ;
392                            CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                        IN  ¸

        try {
          serverSocket = new ServerSocket ( PORT ) ;
          while ( true ) {
            System . out . println ( " Asteptam un client ... " ) ;
            Socket socket = serverSocket . accept () ;

             // Executam solicitarea clientului intr - un fir de
                  executie
             ClientThread t = new ClientThread ( socket ) ;
             t . start () ;
          }
        } catch ( IOException e ) {
          System . err . println ( " Eroare IO  n " + e ) ;
        } finally {
          serverSocket . close () ;
        }
    }

    public static void main ( String [] args ) throws IOException {
      SimpleServer server = new SimpleServer () ;
    }
}




              Listing 13.3: Structura unui client bazat pe conexiuni
import java . net .*;
import java . io .*;

public class SimpleClient {

    public static void main ( String [] args ) throws IOException {
      // Adresa IP a serverului
      String adresaServer = " 127.0.0.1 " ;

        // Portul la care serverul ofera serviciul
        int PORT = 8100;

        Socket socket = null ;
        PrintWriter out = null ;
        BufferedReader in = null ;
        String cerere , raspuns ;

        try {
          socket = new Socket ( adresaServer , PORT ) ;
13.5. COMUNICAREA PRIN DATAGRAME                                           393

          out = new PrintWriter ( socket . getOutputStream () , true ) ;
          in = new BufferedReader ( new Inpu tStream Reader (
             socket . getInputStream () ) ) ;

          // Trimitem o cerere la server
          cerere = " Duke " ;
          out . println ( cerere ) ;

          // Asteaptam raspunsul de la server (" Hello Duke !")
          raspuns = in . readLine () ;
          System . out . println ( raspuns ) ;

        } catch ( UnknownHostExc e p t i o n e ) {
          System . err . println ( " Serverul nu poate fi gasit  n " + e )
             ;
          System . exit (1) ;
        } finally {
          if ( out != null )
            out . close () ;
          if ( in != null )
            in . close () ;
          if ( socket != null )
            socket . close () ;
        }
    }
}




13.5         Comunicarea prin datagrame
In acest model nu exist˘ o conexiune permanent˘ ˆ
                         a                          a ıntre client ¸i server prin
                                                                   s
intermediul c˘reia s˘ se realizeze comunicarea. Clientul trimite cererea c˘tre
               a     a                                                       a
server prin intermediul unuia sau mai multor pachete de date independente,
serverul le receptioneaz˘, extrage informatiile continute ¸i returneaz˘ r˘spunsul
                 ¸      a                 ¸        ¸      s            a a
tot prin intermediul pachetelor. Un astfel de pachet se nume¸te datagram˘
                                                                  s             a
¸i este reprezentat printr-un obiect din clasa DatagramPacket. Rutarea
s
datagramelor de la o ma¸in˘ la alta se face exclusiv pe baza informatiilor
                           s a                                              ¸
continute de acestea. Primirea ¸i trimiterea datagramelor se realizeaz˘ prin
    ¸                            s                                         a
intermediul unui socket, modelat prin intermediul clasei DatagramSocket.
394                         CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                     IN  ¸




    Dup˘ cum am mentionat deja, dezavantajul acestei metode este c˘ nu
        a               ¸                                                   a
garanteaz˘ ajungerea la destinatie a pachetelor trimise ¸i nici c˘ vor fi primite
           a                      ¸                       s      a
ˆ aceea¸i ordinie ˆ care au fost expediate. Pe de alt˘ parte, exist˘ situatii
ın       s        ın                                     a             a      ¸
ˆ care aceste lucruri nu sunt importante ¸i acest model este de preferat celui
ın                                         s
bazat pe conexiuni care solicit˘ mult mai mult atˆt serverul cˆt ¸i clientul. De
                               a                   a           a s
fapt, protocolul TCP/IP folose¸te tot pachete pentru trimiterea informatiilor
                                 s                                         ¸
dintr-un nod ˆ altul al retelei, cu deosebirea c˘ asigur˘ respectarea ordinii de
              ın           ¸                    a       a
transmitere a mesajelor ¸i verific˘ ajungerea la destinatie a tuturor pachetelor
                         s         a                    ¸
- ˆ cazul ˆ care unul nu a ajuns, acesta va fi retrimis automat.
  ın       ın
    Clasa DatagramPacket contine urm˘torii constructori:
                                 ¸       a

  DatagramPacket(byte[] buf,        int length,
    InetAddress address, int        port)
  DatagramPacket(byte[] buf,        int offset, int length,
    InetAddress address, int        port)

  DatagramPacket(byte[] buf, int offset, int length,
    SocketAddress address)
  DatagramPacket(byte[] buf, int length,
    SocketAddress address)

  DatagramPacket(byte[] buf, int length)
  DatagramPacket(byte[] buf, int offset, int length)

   Primele dou˘ perechi de constructori sunt pentru creare pachetelor ce
               a
vor fi expediate, diferenta ˆ
                         ¸ ıntre ele fiind utilizarea claselor InetAddress,
respectiv SocketAddress pentru specificarea adresei desinatie.¸
   A trei pereche de constructori este folosit˘ pentru crearea unui pachet ˆ
                                              a                            ın
care vor fi receptionate date, ei nespecificˆnd vreo surs˘ sau destinatie.
                ¸                           a             a            ¸
13.5. COMUNICAREA PRIN DATAGRAME                                          395

    Dup˘ crearea unui pachet procesul de trimitere ¸i primire a acestuia im-
         a                                            s
plic˘ apelul metodelor send ¸i receive ale clasei DatagramSocket. Deoarece
    a                         s
toate informatii sunt incluse ˆ datagram˘, acela¸i socket poate fi folosit atˆt
              ¸               ın          a       s                          a
pentru trimiterea de pachete, eventual c˘tre destinatii diferite, cˆt ¸i pentru
                                         a            ¸            a s
receptionarea acestora de la diverse surse. In cazul ˆ care refolosim pachete,
      ¸                                              ın
putem schimba continutul acestora cu metoda setData, precum ¸i adresa la
                    ¸                                               s
care le trimitem prin setAddress, setPort ¸i setSocketAddress.
                                               s
    Extragerea informatiilor contiunte de un pachet se realizeaz˘ prin metoda
                        ¸        ¸                              a
getData din clasa DatagramPacket. De asemenea, aceast˘ clas˘ ofer˘ metode
                                                           a     a     a
pentru aflarea adresei IP ¸i a portului procesului care a trimis datagrama,
                           s
pentru a-i putea r˘spunde dac˘ este necesar. Acestea sunt: getAdress,
                   a             a
getPort ¸i getSocketAddress.
           s

          Listing 13.4: Structura unui server bazat pe datagrame
import java . net .*;
import java . io .*;

public class DatagramServer {

  public static final int PORT = 8200;
  private DatagramSocket socket = null ;
  DatagramPacket cerere , raspuns = null ;

  public void start () throws IOException {

     socket = new DatagramSocket ( PORT ) ;
     try {
       while ( true ) {

          // Declaram pachetul in care va fi receptionata
             cererea
          byte [] buf = new byte [256];
          cerere = new DatagramPacket ( buf , buf . length ) ;

          System . out . println ( " Asteptam un pachet ... " ) ;
          socket . receive ( cerere ) ;

          // Aflam adresa si portul de la care vine cererea
          InetAddress adresa = cerere . getAddress () ;
          int port = cerere . getPort () ;

          // Construim raspunsul
396                          CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                      IN  ¸

            String mesaj = " Hello " + new String ( cerere . getData ()
               );
            buf = mesaj . getBytes () ;

            // Trimitem un pachet cu raspunsul catre client
            raspuns = new DatagramPacket ( buf , buf . length , adresa ,
                port ) ;
            socket . send ( raspuns ) ;
          }
        } finally {
          if ( socket != null )
            socket . close () ;
        }
    }

    public static void main ( String [] args ) throws IOException {
      new DatagramServer () . start () ;
    }
}




             Listing 13.5: Structura unui client bazat pe datagrame
import java . net .*;
import java . io .*;

public class DatagramClient {

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

        // Adresa IP si portul la care ruleaza serverul
        InetAddress adresa = InetAddress . getByName ( " 127.0.0.1 " ) ;
        int port =8200;

        DatagramSocket socket = null ;
        DatagramPacket packet = null ;
        byte buf [];

        try {
          // Construim un socket pentru comunicare
          socket = new DatagramSocket () ;

          // Construim si trimitem pachetul cu cererea catre
             server
          buf = " Duke " . getBytes () ;
˘
13.6. TRIMITEREA DE MESAJE CATRE MAI MULTI CLIENTI
                                        ¸       ¸                           397

          packet = new DatagramPacket ( buf , buf . length , adresa ,
             port ) ;
          socket . send ( packet ) ;

          // Asteaptam pachetul cu raspunsul de la server
          buf = new byte [256];
          packet = new DatagramPacket ( buf , buf . length ) ;
          socket . receive ( packet ) ;

          // Afisam raspunsul (" Hello Duke !")
          System . out . println ( new String ( packet . getData () ) ) ;
        } finally {
          if ( socket != null )
            socket . close () ;
        }
    }
}




13.6         Trimiterea de mesaje c˘tre mai multi
                                   a           ¸
             clienti
                  ¸

Diverse situatii impun gruparea mai multor clienti astfel ˆ at un mesaj (pa-
             ¸                                   ¸        ıncˆ
chet) trimis pe adresa grupului s˘ fie receptionat de fiecare dintre ace¸tia.
                                  a         ¸                           s
Gruparea mai multor programe ˆ vederea trimiterii multiple de mesaje se
                                 ın
realizeaz˘ prin intermediul unui socket special, descris de clasa Multicast-
         a
Socket, extensie a clasei DatagramSocket.

   Un grup de clienti abonati pentru trimitere multipl˘ este specificat printr-
                   ¸        ¸                         a
o adres˘ IP din intervalul 224.0.0.1 - 239.255.255.255 ¸i un port UDP.
       a                                                     s
Adresa 224.0.0.0 este rezervat˘ ¸i nu trebuie folosit˘.
                                as                   a
398                         CAPITOLUL 13. PROGRAMARE ˆ RETEA
                                                     IN  ¸




             Listing 13.6: Inregistrarea unui client ˆ
                                                     ıntr-un grup
import java . net .*;
import java . io .*;

public class MulticastClient {

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

      // Adresa IP si portul care reprezinta grupul de clienti
      InetAddress group = InetAddress . getByName ( " 230.0.0.1 " ) ;
      int port =4444;

      MulticastSocket socket = null ;
      byte buf [];

      try {
        // Ne alaturam grupului aflat la adresa si portul
            specificate
        socket = new MulticastSocket ( port ) ;
        socket . joinGroup ( group ) ;

        // Asteaptam un pachet venit pe adresa grupului
        buf = new byte [256];
        DatagramPacket packet = new DatagramPacket ( buf , buf .
           length ) ;

        System . out . println ( " Asteptam un pachet ... " ) ;
        socket . receive ( packet ) ;
˘
13.6. TRIMITEREA DE MESAJE CATRE MAI MULTI CLIENTI
                                        ¸       ¸                             399

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



              Listing 13.7: Transmiterea unui mesaj c˘tre un grup
                                                     a
import java . net .*;
import java . io .*;

public class MulticastSend {

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

        InetAddress grup = InetAddress . getByName ( " 230.0.0.1 " ) ;
        int port = 4444;
        byte [] buf ;
        DatagramPacket packet = null ;

        // Cream un socket cu un numar oarecare
        DatagramSocket socket = new DatagramSocket (0) ;
        try {
          // Trimitem un pachet catre toti clientii din grup
          buf = ( new String ( " Salut grup ! " ) ) . getBytes () ;
          packet = new DatagramPacket ( buf , buf . length , grup , port
              );
          socket . send ( packet ) ;

        } finally {
          socket . close () ;
        }
    }
}
400   CAPITOLUL 13. PROGRAMARE ˆ RETEA
                               IN  ¸
Capitolul 14

Appleturi

14.1       Introducere

Definitie¸
    Un applet reprezint˘ un program Java de dimensiuni reduse ce gestioneaz˘
                       a                                                   a
o suprafat˘ de afi¸are (container) care poate fi inclus˘ ˆ
          ¸a      s                                  a ıntr-o pagin˘ Web. Un
                                                                   a
astfel de program se mai nume¸te miniaplicatie.
                               s

    Ca orice alt˘ aplicatie Java, codul unui applet poate fi format din una sau
                 a       ¸
mai multe clase. Una dintre acestea este principal˘ ¸i extinde clasa Applet,
                                                     as
aceasta fiind clasa ce trebuie specificat˘ ˆ documentul HTML ce descrie
                                           a ın
pagina Web ˆ care dorim s˘ includem appletul.
              ın                a
    Diferenta fundamental˘ dintre un applet ¸i o aplicatie const˘ ˆ faptul c˘
           ¸                  a                 s        ¸       a ın         a
un applet nu poate fi executat independent, ci va fi executat de browserul
ˆ care este ˆ arcat˘ pagina Web ce contine appletul respectiv. O aplicatie
ın           ınc˘     a                     ¸                               ¸
independent˘ este executat˘ prin apelul interpretorului java, avˆnd ca ar-
             a                  a                                   a
gument numele clasei principale a aplicatiei, clasa principal˘ fiind cea care
                                            ¸                  a
contine metoda main. Ciclul de viat˘ al unui applet este complet diferit,
    ¸                                    a
fiind dictat de evenimentele generate de c˘tre browser la vizualizarea docu-
                                              a
mentului HTML ce contine appletul.
                           ¸
    Pachetul care ofer˘ suport pentru crearea de appleturi este java.applet,
                        a
cea mai important˘ clas˘ fiind Applet. In pachetul javax.swing exist˘
                    a        a                                                a
¸i clasa JApplet, care extinde Applet, oferind suport pentru crearea de
s
appleturi pe arhitectura de componente JFC/Swing.

                                     401
402                                        CAPITOLUL 14. APPLETURI

   Ierarhia claselor din care deriv˘ appleturile este prezentata ˆ figura de
                                   a                             ın
mai jos:




   Fiind derivat˘ din clasa Container, clasa Applet descrie de fapt suprafete
                a                                                          ¸
de afi¸are, asemenea claselor Frame sau Panel.
     s


14.2      Crearea unui applet simplu
Crearea structurii de fi¸iere ¸i compilarea applet-urilor sunt identice ca ˆ
                         s     s                                          ın
cazul aplicatiilor. Difer˘ ˆ schimb structura programului ¸i modul de rulare
            ¸            a ın                             s
a acestuia. S˘ parguream ˆ continuare ace¸ti pa¸i pentru a realiza un applet
              a             ın             s     s
extrem de simplu, care afi¸eaz˘ o imagine ¸i un ¸ir de caractere.
                            s a            s     s


   1. Scrierea codului sursa


import java.awt.* ;
import java.applet.* ;

public class FirstApplet extends Applet {
  Image img;
  public void init() {
    img = getImage(getCodeBase(), "taz.gif");
  }
14.2. CREAREA UNUI APPLET SIMPLU                                            403

    public void paint (Graphics g) {
      g.drawImage(img, 0, 0, this);
      g.drawOval(100,0,150,50);
      g.drawString("Hello! My name is Taz!", 110, 25);
    }
}

   Pentru a putea fi executat˘ de browser, clasa principal˘ a appletului tre-
                            a                            a
buie s˘ fie public˘.
      a          a


    2. Salvarea fisierelor surs˘
                              a

    Ca orice clas˘ public˘, clasa principala a appletului va fi salvat˘ ˆ
                 a       a                                           a ıntr-un
fi¸ier cu acela¸i nume ¸i extensia .java. A¸adar, vom salva clasa de mai sus
  s           s       s                     s
ˆ
ıntr-un fi¸ier FirstApplet.java.
         s


    3. Compilarea

    Compilarea se face la fel ca ¸i la aplicatiile independente, folosind compi-
                                 s           ¸
latorul javac apelat pentru fi¸ierul ce contine appletul.
                               s             ¸

    javac FirstApplet.java

In cazul ˆ care compilarea a reu¸it va fi generat fisierul FirstApplet.class.
         ın                     s


    4. Rularea appletului

    Applet-urile nu ruleaza independent. Ele pot fi rulate doar prin inter-
mediul unui browser: Internet Explorer, Netscape, Mozilla, Opera, etc. sau
printr-un program special cum ar fi appletviewer din kitul de dezvoltare
J2SDK. Pentru a executa un applet trebuie s˘ facem dou˘ operatii:
                                            a          a      ¸

    • Crearea unui fi¸ier HTML ˆ care vom include applet-ul. S˘ con-
                       s            ın                          a
      sider˘m fi¸ierul simplu.html, avˆnd continutul de mai jos:
           a   s                      a     ¸
404                                         CAPITOLUL 14. APPLETURI

      <html>
      <head>
        <title>Primul applet Java</title>
      </head>
      <body>
        <applet code=FirstApplet.class width=400 height=400>
        </applet>
      </body>
      </html>

   • Vizualizarea appletului: se deschide fisierul simplu.html folosind
     unul din browser-ele amintite sau efectuˆnd apelul:
                                             a
     appletviewer simplu.html.


14.3      Ciclul de viat˘ al unui applet
                       ¸a
Executia unui applet ˆ
      ¸               ıncepe ˆ momentul ˆ care un browser afi¸eaz˘ o pagin˘
                             ın           ın                   s a           a
Web ˆ care este inclus appletul respectiv ¸i poate trece prin mai multe etape.
     ın                                   s
Fiecare etap˘ este strˆns legat˘ de un eveniment generat de c˘tre browser ¸i
            a         a         a                              a            s
determin˘ apelarea unei metode specifice din clasa ce implementeaz˘ apple-
         a                                                            a
tul.

   • Inc˘rcarea ˆ memorie
        a        ın
     Este creat˘ o instanta a clasei principale a appletului ¸i ˆ
               a         ¸                                   s ıncarcat˘ ˆ
                                                                       a ın
     memorie.

   • Initializarea
         ¸
     Este apelat˘ metoda init ce permite initializarea diverselor variabile,
                 a                           ¸
     citirea unor parametri de intrare, etc.

   • Pornirea
     Este apelat˘ metoda start
                a

   • Executia propriu-zis˘
            ¸                  a
     Const˘ ˆ interactiunea dintre utilizator ¸i componentele afi¸ate pe
           a ın          ¸                        s                    s
     suprafata appletului sau ˆ executarea unui anumit cod ˆ
            ¸                    ın                              ıntr-un fir de
     executie. In unele situatii ˆ
          ¸                    ¸ ıntreaga executie a appletului se consum˘ la
                                               ¸                          a
     etapele de initializare ¸i pornire.
                   ¸         s
¸˘
14.3. CICLUL DE VIATA AL UNUI APPLET                                      405

   • Oprirea temporar˘     a
     In cazul ˆ care utilizatorul p˘r˘se¸te pagina Web ˆ care ruleaz˘ apple-
              ın                   aa s                ın            a
     tul este apelat˘ metoda stop a acestuia, dˆndu-i astfel posibilitatea s˘
                    a                            a                          a
     opreasca temporar executia sa pe perioada ˆ care nu este vizibil, pentru
                              ¸                  ın
     a nu consuma inutil din timpul procesorului. Acela¸i lucru se ˆ ampl˘
                                                         s         ıntˆ     a
     dac˘ fereastra browserului este minimizat˘. In momentul cˆnd pagina
         a                                      a                a
     Web ce contine appletul devine din nou activ˘, va fi reapelat˘ metoda
                                                    a              a
     start.

   • Oprirea definitiv˘  a
     La ˆ
        ınchiderea tuturor instantelor browserului folosit pentru vizualizare,
                                 ¸
     appletul va fi eliminat din memorie ¸i va fi apelat˘ metoda destroy a
                                          s             a
     acestuia, pentru a-i permite s˘ elibereze resursele detinute. Apelul
                                     a                        ¸
     metodei destroy este ˆ ıntotdeauna precedat de apelul lui stop.



   Metodele specifice appleturilor

   A¸adar, exist˘ o serie de metode specifice appleturilor ce sunt apelate au-
     s           a
tomat la diverse evenimente generate de c˘tre browser. Acestea sunt definite
                                         a
ˆ clasa Applet ¸i sunt enumerate ˆ tabelul de mai jos:
ın              s                 ın

    Metoda Situatia ˆ care este apelat˘
                    ¸ ın                       a
    init    La initializarea appletului. Teoretic, aceast˘ metod˘
                    ¸                                      a    a
            ar trebui s˘ se apeleze o singur˘ dat˘, la prima
                         a                     a    a
            afi¸are a appletului ˆ pagin˘, ˆ a, la unele browsere,
               s                   ın       a ıns˘
            este posibil ca ea s˘ se apeleze de mai multe ori.
                                 a
    start   Imediat dup˘ initializare ¸i de fiecare dat˘
                           a ¸            s             a
            cˆnd appletul redevine activ, dup˘ o oprire temporar˘.
             a                                    a               a
    stop    De fiecare dat˘ cˆnd appletul nu mai este vizibil
                            a a
            (pagina Web nu mai este vizibil˘, fereastra browserului
                                                a
            este minimizat˘, etc) ¸i ˆ
                             a        s ınainte de destroy.
    destroy La ˆ ınchiderea ultimei instante a browserului
                                            ¸
            care a ˆ arcat ˆ memorie clasa principal˘ a appletului.
                     ınc˘     ın                         a



   Atentie
       ¸
406                                         CAPITOLUL 14. APPLETURI

   Aceste metode sunt apelate automat de browser ¸i nu trebuie apelate
                                                 s
explicit din program !




    Structura general˘ a unui applet
                     a


import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class StructuraApplet extends Applet {

    public void init() {
    }

    public void start() {
    }

    public void stop() {
    }

    public void destroy() {
    }

}


14.4      Interfata grafic˘ cu utilizatorul
                 ¸       a
Dup˘ cum am v˘zut, clasa Applet este o extensie a superclasei Container,
     a           a
ceea ce ˆ
        ınseamn˘ c˘ appleturile sunt, ˆ
                 a a                     ınainte de toate, suprafete de afi¸are.
                                                                   ¸       s
Plasarea componentelor, gestionarea pozition˘rii lor ¸i tratarea evenimentelor
                                            ¸ a         s
generate se realizeaz˘ la fel ca ¸i ˆ cazul aplicatiilor. Uzual, ad˘ugarea com-
                     a           s ın             ¸                a
ponentelor pe suprafata appletului precum ¸i stabilirea obiectelor respons-
                       ¸                        s
abile cu tratarea evenimentelor generate sunt operatiuni ce vor fi realizate ˆ
                                                       ¸                     ın
metoda init.
¸        ˘
14.4. INTERFATA GRAFICA CU UTILIZATORUL                               407

   Gestionarul de pozitionare implicit este FlowLayout, ˆ a acesta poate fi
                      ¸                                 ıns˘
schimbat prin metoda setLayout.
408                                         CAPITOLUL 14. APPLETURI

   Desenarea pe suprafata unui applet
                              ¸
   Exist˘ o categorie ˆ
           a            ıntreag˘ de appleturi ce nu comunic˘ cu utilizatorul
                                a                             a
prin intermediul componentelor ci, executia lor se rezum˘ la diverse operatiuni
                                         ¸              a                 ¸
de desenare realizate ˆ metoda paint. Reamintim c˘ metoda paint este re-
                      ın                              a
sponsabil˘ cu definirea aspectului grafic al oric˘rei componente. Implicit,
            a                                     a
metoda paint din clasa Applet nu realizeaz˘ nimic, deci, ˆ cazul ˆ care
                                               a               ın      ın
dorim s˘ desen˘m direct pe suprafata unui applet va fi nevoie s˘ supradefinim
         a    a                     ¸                           a
aceast˘ metod˘.
       a      a
  public void paint(Graphics g) {
    // Desenare
   ...
  }
In cazul ˆ care este aleas˘ aceast˘ solutie, evenimentele tratate uzual vor fi
         ın               a       a     ¸
cele generate de mouse sau tastatur˘.
                                    a


14.5      Definirea ¸i folosirea parametrilor
                   s
Parametrii sunt pentru appleturi ceea ce argumentele de la linia de comand˘   a
sunt pentru aplicatiile independente. Ei permit utilizatorului s˘ personalizeze
                  ¸                                             a
aspectul sau comportarea unui applet f˘r˘ a-i schimba codul ¸i recompila
                                          aa                       s
clasele.
    Definirea parametrilor se face ˆ cadrul tagului APPLET din documen-
                                     ın
tul HTML ce contine appletul ¸i sunt identificati prin atributul PARAM.
                  ¸             s                ¸
Fiecare parametru are un nume, specificat prin NAME ¸i o valoare, speci-
                                                           s
ficat˘ prin VALUE, ca ˆ exemplul de mai jos:
     a                    ın
<APPLET CODE="TestParametri.class" WIDTH=100 HEIGHT=50
  <PARAM NAME=textAfisat VALUE="Salut">
  <PARAM NAME=numeFont VALUE="Times New Roman">
  <PARAM NAME=dimFont VALUE=20>
</APPLET>
Ca ¸i ˆ cazul argumentelor trimise aplicatiilor de la linia de comand˘, tipul
    s ın                                 ¸                            a
parametrilor este ˆ ıntotdeauna ¸ir de caractere, indiferent dac˘ valoarea
                                s                                  a
este ˆ
     ıntre ghilimele sau nu.
    Fiecare applet are ¸i un set de parametri prestabiliti ale c˘ror nume nu
                        s                                ¸      a
vor putea fi folosite pentru definirea de noi parametri folosind metoda de
14.5. DEFINIREA SI FOLOSIREA PARAMETRILOR
                ¸                                                            409

mai sus. Ace¸tia apar direct ˆ corpul tagului APPLET ¸i definesc informatii
               s                   ın                       s                 ¸
generale despre applet. Exemple de astfel de parametri sunt CODE, WIDTH sau
HEIGHT. Lista lor complet˘ va fi prezentata la descrierea tagului APPLET.
                               a
     Folosirea parametrilor primiti de c˘tre un applet se face prin intermediul
                                      ¸      a
metodei getParameter care prime¸te ca argument numele unui parametru
                                         s
¸i returneaz˘ valoarea acestuia. In cazul ˆ care nu exist˘ nici un parametru
s            a                                   ın           a
cu numele specificat, metoda ˆ       ıntoarce null, caz ˆ care programul trebuie
                                                       ın
s˘ atribuie o valoare implicit˘ variabilei ˆ care se dorea citirea respectivului
  a                              a              ın
parametru.
     Orice applet poate pune la dispozitie o ”documentatie” referitoare la para-
                                           ¸              ¸
metrii pe care ˆ suport˘, pentru a veni ˆ ajutorul utilizatorilor care doresc s˘
                ıi        a                  ın                                 a
includ˘ appletul ˆ
        a           ıntr-o pagin˘ Web. Aceasta se realizeaz˘ prin supradefinirea
                                 a                          a
metodei getParameterInfo, care returneaz˘ un vector format din triplete
                                                    a
de ¸iruri. Fiecare element al vectorului este de fapt un vector cu trei elemente
    s
de tip String, cele trei ¸iruri reprezentˆnd numele parametrului, tipul s˘u ¸i
                            s                 a                             a s
o descriere a sa. Informatiile furnizate de un applet pot fi citite din browserul
                             ¸
folosit pentru vizualizare prin metode specifice acestuia. De exemplu, ˆ ap-ın
pletviewer informatiile despre parametri pot fi vizualizate la rubrica Info din
                      ¸
meniul Applet, ˆ Netscape se folose¸te optiunea Page info din meniul View,
                  ın                     s        ¸
etc.
     S˘ scriem un applet care s˘ afi¸eze un text primit ca parametru, folosind
      a                            a s
un font cu numele ¸i dimensiunea specificate de asemenea ca parametri.
                      s

                     Listing 14.1: Folosirea parametrilor
import java . applet . Applet ;
import java . awt .*;

public class TestParametri extends Applet               {

  String text , numeFont ;
  int dimFont ;

  public void init () {
    text = getParameter ( " textAfisat " ) ;
    if ( text == null )
      text = " Hello " ; // valoare implicita

     numeFont = getParameter ( " numeFont " ) ;
     if ( numeFont == null )
       numeFont = " Arial " ;
410                                          CAPITOLUL 14. APPLETURI


        try {
          dimFont = Integer . parseInt ( getParameter ( " dimFont " ) ) ;
        } catch ( Numb erFo r m at Ex ce pt i o n e ) {
          dimFont = 16;
        }
    }

    public void paint ( Graphics g ) {
      g . setFont ( new Font ( numeFont , Font . BOLD , dimFont ) ) ;
      g . drawString ( text , 20 , 20) ;
    }

    public String [][] getParameterInfo () {
      String [][] info = {
         // Nume                Tip             Descriere
         { " textAfisat " , " String " , " Sirul ce va fi afisat " } ,
         { " numeFont " ,   " String " , " Numele fontului " } ,
         { " dimFont " ,    " int " ,    " Dimensiunea fontului " }
      };
      return info ;
    }

}




14.6         Tag-ul APPLET
Sintaxa complet˘ a tagului APPLET, cu ajutorul c˘ruia pot fi incluse apple-
                a                               a
turi ˆ cadrul paginilor Web este:
     ın
<APPLET
    CODE = clasaApplet
    WIDTH = latimeInPixeli
    HEIGHT = inaltimeInPixeli

        [ARCHIVE = arhiva.jar]
        [CODEBASE = URLApplet]
        [ALT = textAlternativ]
        [NAME = numeInstantaApplet]
        [ALIGN = aliniere]
        [VSPACE = spatiuVertical]
14.6. TAG-UL APPLET                                                    411

   [HSPACE = spatiuOrizontal] >

 [< PARAM NAME = parametru1 VALUE = valoare1 >]
 [< PARAM NAME = parametru2 VALUE = valoare2 >]
 ...
 [text HTML alternativ]

</APPLET>

  Atributele puse ˆ
                  ıntre paranteze p˘trate sunt optionale.
                                   a             ¸

  • CODE = clasaApplet
    Numele fi¸ierului ce contine clasa principal˘ a appletului. Acesta va
              s              ¸                   a
    fi c˘utat ˆ directorul specificat de CODEBASE. Nu poate fi absolut ¸i
       a      ın                                                         s
    trebuie obligatoriu specificat. Extensia ”.class” poate sau nu s˘ apar˘.
                                                                   a     a

  • WIDTH =latimeInPixeli, HEIGHT =inaltimeInPixeli
    Specific˘ l˘¸imea ¸i ˆ altimea suprafetei ˆ care va fi afi¸at appletul.
           a at       s ın˘ ¸            ¸ ın              s
    Sunt obligatorii.

  • ARCHIVE = arhiva.jar
    Specific˘ arhiva ˆ care se g˘sesc clasele appletului.
           a        ın         a

  • CODEBASE = directorApplet
    Specific˘ URL-ul la care se g˘se¸te clasa appletului. Uzual se exprim˘
             a                   a s                                    a
    relativ la directorul documentului HTML. In cazul ˆ care lipse¸te, se
                                                        ın          s
    consider˘ implicit URL-ul documentului.
              a

  • ALT = textAlternativ
    Specific˘ textul ce trebuie afi¸at dac˘ browserul ˆ ¸elege tagul APPLET
           a                     s      a           ınt
    dar nu poate rula appleturi Java.

  • NAME =numeInstantaApplet
    Ofer˘ posibilitatea de a da un nume respectivei instante a appletu-
         a                                                  ¸
    lui, astfel ˆ at mai multe appleturi aflate pe aceea¸i pagin˘ s˘ poat˘
                ıncˆ                                   s       a a      a
    comunica ˆ  ıntre ele folosindu-se de numele lor.

  • ALIGN =aliniere
    Semnific˘ modalitatea de aliniere a appletului ˆ pagina Web. Acest
            a                                     ın
    atribut poate primi una din urm˘toarele valori: left, right, top,
                                    a
412                                          CAPITOLUL 14. APPLETURI

      texttop, middle, absmiddle, baseline, bottom, absbottom , seminificatiile
                                                                         ¸
      lor fiind acelea¸i ca ¸i la tagul IMG.
                     s     s

   • VSPACE =spatiuVertical, HSPACE = spatiuOrizontal
     Specific˘ numarul de pixeli dintre applet ¸i marginile suprafetei de
             a                                s
     afi¸are.
       s

   • PARAM
     Tag-urile PARAM sunt folosite pentru specificarea parametrilor unui ap-
     plet (vezi ”Folosirea parametrilor”).

   • text HTML alternativ
     Este textul ce va fi afi¸at ˆ cazul ˆ care browserul nu ˆ
                           s ın        ın                   ıntelege tagul
     APPLET. Browserele Java-enabled vor ignora acest text.


14.7       Folosirea firelor de executie ˆ appleturi
                                    ¸ ın
La ˆ arcarea unei pagini Web, fiec˘rui applet ˆ este creat automat un fir
    ınc˘                                 a             ıi
de executie responsabil cu apelarea metodelor acestuia. Acestea vor rula
            ¸
concurent dup˘ regulile de planificare implementate de ma¸ina virtual˘ Java
                  a                                          s           a
a platformei folosite.
    Din punctul de vedere al interfetei grafice ˆ a, fiecare applet aflat pe o
                                        ¸            ıns˘
pagin˘ Web are acces la un acela¸i fir de executie, creat de asemenea automat
       a                            s               ¸
de c˘tre browser, ¸i care este responsabil cu desenarea appletului (apelul
     a                s
metodelor update ¸i paint) precum ¸i cu transmiterea mesajelor generate
                      s                    s
de c˘tre componente. Intrucˆt toate appleturile de pe pagin˘ ”ˆ
     a                         a                               a ımpart” acest
fir de executie, nici unul nu trebuie s˘ ˆ solicite ˆ mod excesiv, deoarece va
              ¸                           a ıl         ın
provoca functionarea anormal˘ sau chiar blocarea celorlalte.
                ¸                a
    In cazul ˆ care dorim s˘ efectu˘m operatiuni consumatoare de timp este
               ın            a         a          ¸
recomandat s˘ le realiz˘m ˆ
                  a      a ıntr-un alt fir de executie, pentru a nu bloca
                                                           ¸
interactiunea utilizatorului cu appletul, redesenarea acestuia sau activitatea
         ¸
celorlalte appleturi de pe pagin˘. a
    S˘ considerm˘m mai ˆ ai dou˘ abord˘ri gre¸ite de lucru cu appleturi.
      a             a      ıntˆ        a        a        s
Dorim s˘ cre˘m un applet care s˘ afi¸eze la coordonate aleatoare mesajul
           a     a                    a s
”Hello”, cu pauz˘ de o secund˘ ˆ
                    a            a ıntre dou˘ afi¸˘ri. Prima variant˘, gre¸it˘ de
                                               a sa                a     s a
altfel, ar fi:
14.7. FOLOSIREA FIRELOR DE EXECUTIE ˆ APPLETURI
                                ¸ IN                                    413

              Listing 14.2: Incorect: blocarea metodei paint
import java . applet .*;
import java . awt .*;

public class AppletRau1 extends Applet {
  public void paint ( Graphics g ) {
    while ( true ) {
      int x = ( int ) ( Math . random () * getWidth () ) ;
      int y = ( int ) ( Math . random () * getHeight () ) ;
      g . drawString ( " Hello " , x , y ) ;
      try {
          Thread . sleep (1000) ;
      } catch ( InterruptedE xc ep ti on e ) {}
    }
  }
}

   Motivul pentru care acest applet nu functioneaz˘ corect ¸i probabil va
                                              ¸       a       s
duce la anomalii ˆ functionarea browserului este c˘ firul de executie care
                 ın      ¸                            a              ¸
se ocup˘ cu desenarea va r˘mˆne blocat ˆ metoda paint, ˆ
       a                    a a            ın                ıncercˆnd s˘ o
                                                                   a     a
termine. Ca regul˘ general˘, codul metodei paint trebuie s˘ fie cˆt mai
                   a        a                                  a      a
simplu de executat ceea ce, evident, nu este cazul ˆ appletul de mai sus.
                                                   ın
   O alt˘ idee de rezolvare care ne-ar putea veni, de asemenea gre¸it˘, este
        a                                                          s a
urm˘toarea :
    a

          Listing 14.3: Incorect: appletul nu termin˘ initializarea
                                                    a ¸
import java . applet .*;
import java . awt .*;

public class AppletRau2 extends Applet {
  int x , y ;

  public void init () {
    while ( true ) {
      x = ( int ) ( Math . random () * getWidth () ) ;
      y = ( int ) ( Math . random () * getHeight () ) ;
      repaint () ;
      try {
        Thread . sleep (1000) ;
      } catch ( InterruptedE x ce pt io n e ) {}
    }
  }
  public void paint ( Graphics g ) {
414                                              CAPITOLUL 14. APPLETURI

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

   Pentru a putea da o solutie corect˘ problemei propuse, trebuie s˘ folosim
                               ¸         a                             a
un fir de executie propriu. Structura unui applet care doreste s˘ lanseze un
                 ¸                                                   a
fir de executie poate avea dou˘ forme. In prima situatie appletul porne¸te
             ¸                    a                         ¸                  s
firul la initialzarea sa iar acesta va rula, indiferent dac˘ appletul mai este sau
           ¸                                              a
nu vizibil, pˆn˘ la oprirea sa natural˘ (terminarea metodei run) sau pˆn˘ la
              a a                       a                                  a a
ˆ
ınchiderea sesiunii de lucru a browserului.

            Listing 14.4: Corect: folosirea unui fir de executie propriu
                                                            ¸
import java . applet .*;
import java . awt .*;

public class AppletCorect1 extends Applet implements Runnable
    {
  int x , y ;
  Thread fir = null ;

    public void init () {
      if ( fir == null ) {
        fir = new Thread ( this ) ;
        fir . start () ;
      }
    }

    public void run () {
      while ( true ) {
          x = ( int ) ( Math . random () * getWidth () ) ;
          y = ( int ) ( Math . random () * getHeight () ) ;
          repaint () ;
          try {
            Thread . sleep (1000) ;
          } catch ( Inter ru pt ed Ex ce pt io n e ) {}
      }
    }
    public void paint ( Graphics g ) {
      g . drawString ( " Hello " , x , y ) ;
    }
}

    In cazul ˆ care firul de executie pornit de applet efectueaz˘ operatii ce
             ın                  ¸                             a
14.7. FOLOSIREA FIRELOR DE EXECUTIE ˆ APPLETURI
                                ¸ IN                                    415

au sens doar dac˘ appletul este vizibil, cum ar fi animatie, ar fi de dorit
                   a
ca acesta s˘ se opreasca atunci cˆnd appletul nu mai este vizibil (la apelul
           a                      a
metodei stop) ¸i s˘ reporneasca atunci cˆnd appletul redevine vizibil (la
                 s a                       a
apelul metodei start). Un applet este considerat activ imediat dup˘ apelul
                                                                    a
metodei start ¸i devine inactiv la apelul metodei stop. Pentru a afla dac˘
                 s                                                         a
un applet este activ se folose¸te metoda isActive.
                              s
   S˘ modific˘m programul anterior, ad˘ugˆnd ¸i un contor care s˘ numere
     a         a                         a a s                    a
afi¸˘rile de mesaje - acesta nu va fi incrementat pe perioada ˆ care appletul
  sa                                                        ın
nu este activ.

              Listing 14.5: Folosirea metodelor start ¸i stop
                                                      s
import java . applet .*;
import java . awt .*;

public class AppletCorect2 extends Applet implements Runnable
    {
  int x , y ;
  Thread fir = null ;
  boolean activ = false ;
  int n = 0;

  public void start () {
    if ( fir == null ) {
      fir = new Thread ( this ) ;
      activ = true ;
      fir . start () ;
    }
  }

  public void stop () {
    activ = false ;
    fir = null ;
  }

  public void run () {
    while ( activ ) {
      x = ( int ) ( Math . random () * getWidth () ) ;
      y = ( int ) ( Math . random () * getHeight () ) ;
      n ++;
      repaint () ;
      try {
        Thread . sleep (1000) ;
      } catch ( InterruptedE x c e p t i o n e ) {}
416                                          CAPITOLUL 14. APPLETURI

        }
    }

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




   Atentie
         ¸
   Este posibil ca unele browsere s˘ nu apele metoda stop ˆ situatiile
                                     a                           ın     ¸
prev˘zute ˆ specficiatiile appleturilor. Din acest motiv, corectitudinea unui
    a      ın         ¸
applet nu trebuie s˘ se bazeze pa acest mecanism.
                   a




14.8        Alte metode oferite de clasa Applet
Pe lˆng˘ metodele de baz˘: init, start, stop, destroy, clasa Applet
    a a                  a
ofer˘ metode specifice applet-urilor cum ar fi:
    a


   Punerea la dispozitie a unor informatii despre applet
                                               ¸
Similar˘ cu metoda getParameterInfo ce oferea o ”documentatie” despre
       a                                                         ¸
parametrii pe care ˆ accept˘ un applet, exist˘ metoda getAppletInfo ce per-
                   ıi      a                 a
mite specificarea unor informatii legate de applet cum ar fi numele, autorul,
                               ¸
versiunea, etc. Metoda returneaz˘ un sir de caractere continˆnd informatiile
                                 a                          a          ¸
respective.

    public String getAppletInfo() {
      return "Applet simplist, autor necunoscut, ver 1.0";
    }



   Aflarea adreselor URL referitoare la applet
Se realizeaz˘ cu metodele:
            a
14.8. ALTE METODE OFERITE DE CLASA APPLET                                417

   • getCodeBase - ce returneaz˘ URL-ul directorului ce contine clasa
                               a                           ¸
     appletului;

   • getDocumentBase - returneaz˘ URL-ul directorului ce contine doc-
                                  a                         ¸
     umentul HTML ˆ care este inclus appletul respectiv.
                  ın

Aceste metode sunt foarte utile deoarece permit specificarea relativ˘ a unor
                                                                   a
fi¸iere folosite de un applet, cum ar fi imagini sau sunete.
 s


   Afi¸area unor mesaje ˆ bara de stare a browserului
       s                     ın
Acest lucru se realizeaz˘ cu metoda showStatus
                        a

  public void init() {
    showStatus("Initializare applet...");
  }



    Afi¸area imaginilor
       s
Afi¸area imaginilor ˆ
   s                  ıntr-un applet se face fie prin intermediul unei compo-
nente ce permite acest lucru, cum ar fi o suprafat˘ de desenare de tip Canvas,
                                                 ¸a
fie direct ˆ metoda paint a applet-ului, folosind metoda drawImage a clasei
          ın
Graphics. In ambele cazuri, obtinerea unei referinte la imaginea respectiv˘
                                 ¸                   ¸                      a
se va face cu ajutorul metodei getImage din clasa Applet. Aceasta poate
primi ca argument fie adresa URL absolut˘ a fi¸ierului ce reprezint˘ imag-
                                             a    s                   a
inea, fie calea relativ˘ la o anumit˘ adres˘ URL, cum ar fi cea a directorului
                       a            a      a
ˆ care se g˘se¸te documentul HTML ce contine appletul (getDocumentBase)
ın          a s                              ¸
sau a directorului ˆ care se g˘se¸te clasa appletului (getCodeBase).
                    ın         a s

                      Listing 14.6: Afi¸area imaginilor
                                      s
import java . applet . Applet ;
import java . awt .*;

public class Imagini extends Applet           {
  Image img = null ;

  public void init () {
    img = getImage ( getCodeBase () , " taz . gif " ) ;
  }
418                                            CAPITOLUL 14. APPLETURI


    public void paint ( Graphics g ) {
      g . drawImage ( img , 0 , 0 , this ) ;
    }
}




    Aflarea contextului de executie   ¸
Contextul de executie al unui applet se refer˘ la pagina ˆ care acesta ruleaz˘,
                    ¸                        a           ın                  a
eventual ˆ
         ımpreun˘ cu alte appleturi, ¸i este descris de interfata AppletCon-
                  a                  s                         ¸
text. Crearea unui obiect ce implementeaz˘ aceast˘ interfat˘ se realizeaz˘ de
                                            a       a        ¸a            a
c˘tre browser, la apelul metodei getAppletContext a clasei Applet. Prin
 a
intermediul acestei interfete un applet poate ”vedea” ˆ jurul sau, putˆnd
                           ¸                              ın               a
comunica cu alte applet-uri aflate pe aceeasi pagin˘ sau cere browser-ului s˘
                                                     a                        a
deschid˘ diverse documente.
       a
    AppletContext contex = getAppletContext();



   Afi¸area unor documente ˆ browser
       s                       ın
Se face cu metoda showDocument ce prime¸te adresa URL a fi¸ierului ce
                                             s                  s
contine documentul pe care dorim sa-l deschidem (text, html, imagine, etc).
   ¸
Aceast˘ metod˘ este accesat˘ prin intermediul contextului de executie al
       a      a             a                                        ¸
appletului.
    try {
      URL doc = new URL("http://www.infoiasi.ro");
      getAppletContext().showDocument(doc);
    } catch(MalformedURLException e) {
      System.err.println("URL invalid! n" + e);
    }



   Comunicarea cu alte applet-uri
Aceast˘ comunicare implic˘ de fapt identificarea unui applet aflat pe aceea¸i
       a                   a                                               s
pagina ¸i apelarea unei metode sau setarea unei variabile publice a acestuia.
        s
Identificarea se face prin intermediu numelui pe care orice instanta a unui
                                                                   ¸
applet ˆ poate specifica prin atributul NAME.
       ıl
14.8. ALTE METODE OFERITE DE CLASA APPLET                              419

    Obtinerea unei referinte la un applet al c˘rui nume ˆ cunoa¸tem sau
      ¸                   ¸                   a          ıl      s
obtinerea unei enumer˘ri a tuturor applet-urilor din pagin˘ se fac prin in-
  ¸                   a                                    a
termediul contextului de executie, folosind metodele getApplet, respectiv
                               ¸
getApplets.


    Redarea sunetelor
Clasa Applet ofer˘ ¸i posibilitatea red˘rii de sunete ˆ format .au. Acestea
                   as                  a              ın
sunt descrise prin intermediul unor obiecte ce implementeaz˘ interfata Au-
                                                            a       ¸
dioClip din pachetul java.applet. Pentru a reda un sunet aflat ˆ     ıntr-un
fi¸ier ”.au” la un anumit URL exist˘ dou˘ posibilit˘¸i:
 s                                  a     a         at

   • Folosirea metodei play din clasa Applet care prime¸te ca argument
                                                        s
     URL-ul la care se afl˘ sunetul; acesta poate fi specificat absolut sau
                          a
     relativ la URL-ul appletului

   • Crearea unui obiect de tip AudioClip cu metoda getAudioClip apoi
     apelarea metodelor start, loop ¸i stop pentru acesta.
                                    s


                      Listing 14.7: Redarea sunetelor
import java . applet .*;
import java . awt .*;
import java . awt . event .*;

public class Sunete extends Applet implements ActionListener {
  Button play = new Button ( " Play " ) ;
  Button loop = new Button ( " Loop " ) ;
  Button stop = new Button ( " Stop " ) ;
  AudioClip clip = null ;

  public void init () {
    // Fisierul cu sunetul trebuie sa fie in acelasi
    // director cu appletul
    clip = getAudioClip ( getCodeBase () , " sunet . au " ) ;
    add ( play ) ;
    add ( loop ) ;
    add ( stop ) ;

     play . addActionListener ( this ) ;
     loop . addActionListener ( this ) ;
     stop . addActionListener ( this ) ;
420                                           CAPITOLUL 14. APPLETURI

    }

    public void actionPerformed ( ActionEvent e ) {
      Object src = e . getSource () ;
      if ( src == play )
        clip . play () ;
      else if ( src == loop )
        clip . loop () ;
      else if ( src == stop )
        clip . stop () ;
    }
}


   In cazul ˆ care appletul folose¸te mai multe tipuri de sunete, este reco-
            ın                    s
mandat ca ˆ arcarea acestora s˘ fie f˘cut˘ ˆ
           ınc˘                 a     a a ıntr-un fir de executie separat,
                                                                ¸
pentru a nu bloca temporar activitatea fireasc˘ a programului.
                                             a


14.9       Arhivarea appleturilor
Dup˘ cum am v˘zut, pentru ca un applet aflat pe o pagin˘ Web s˘ poat˘
     a           a                                             a        a       a
fi executat codul s˘u va fi transferat de pe serverul care g˘zduie¸te pagina
                    a                                         a       s
Web solicitat˘ pe ma¸ina clientului. Deoarece transferul datelor prin retea
              a        s                                                     ¸
este un proces lent, cu cˆt dimensiunea fi¸ierlor care formeaz˘ appletul este
                           a               s                     a
mai redus˘, cu atˆ ˆ arcarea acestuia se va face mai repede. Mai mult, dac˘
          a       a ınc˘                                                        a
appletul contine ¸i alte clase ˆ afar˘ de cea principal˘ sau diverse resurse
             ¸    s            ın     a                  a
(imagini, sunete, etc), acestea vor fi transferate prin retea abia ˆ momentul
                                                         ¸         ın
ˆ care va fi nevoie de ele, oprind temporar activitatea appletului pˆn˘ la
ın                                                                        a a
ˆ arcarea lor. Din aceste motive, cea mai eficient˘ modalitate de a distribui
ınc˘                                               a
un applet este s˘ arhiv˘m toate fi¸ierele necesare acestuia.
                a        a         s
    Arhivarea fi¸ierelor unui applet se face cu utilitarul jar, oferit ˆ
                  s                                                            ın
distributia J2SDK.
        ¸

    // Exemplu
    jar cvf arhiva.jar ClasaPrincipala.class AltaClasa.class
            imagine.jpg sunet.au
    // sau
    jar cvf arhiva.jar *.class *.jpg *.au
14.10. RESTRICTII DE SECURITATE
              ¸                                                              421

    Includerea unui applet arhivat ˆ   ıntr-o pagin˘ Web se realizeaz˘ spe-
                                                   a                  a
cific˘nd pe lˆng˘ numele clasei principale ¸i numele arhivei care o contine:
     a      a a                           s                           ¸
  <applet archive=arhiva.jar code=ClasaPrincipala
          width=400 height=200 />


14.10       Restrictii de securitate
                   ¸
Deoarece un applet se execut˘ pe ma¸ina utilizatorului care a solicitat pag-
                               a        s
ina Web ce contine appletul respectiv, este foarte important s˘ existe anu-
                 ¸                                                a
mite restrictii de securitate care s˘ controleze activitatea acestuia, pentru a
             ¸                       a
preveni actiuni r˘u intentionate, cum ar fi ¸tergeri de fi¸iere, etc., care s˘
           ¸       a       ¸                   s              s               a
aduc˘ prejudicii utilizatorului. Pentru a realiza acest lucru, procesul care
     a
ruleaz˘ appleturi instaleaz˘ un manager de securitate, adic˘ un obiect de
       a                     a                                  a
tip SecurityManager care va ”superviza” activitatea metodelor appletului,
aruncˆnd exceptii de tip Security Exception ˆ cazul ˆ care una din aces-
      a          ¸                                ın       ın
tea ˆ
    ıncearc˘ s˘ efectueze o operatie nepermis˘.
           a a                     ¸           a
    Un applet nu poate s˘:a
   • Citeasc˘ sau s˘ scrie fi¸iere pe calculatorul pe care a fost ˆ
             a     a        s                                    ıncarcat
     (client).

   • Deschid˘ conexiuni cu alte ma¸ini ˆ afar˘ de cea de pe care provine
             a                    s ın       a
     (host).

   • Porneasc˘ programe pe ma¸ina client.
             a               s

   • Citeasc˘ diverse propriet˘¸i ale sistemului de operare al clientului.
            a                 at
Ferestrele folosite de un applet, altele decˆt cea a browserului, vor ar˘ta
                                               a                            a
altfel decˆt ˆ
          a ıntr-o aplicatie obi¸nuit˘, indicˆnd faptul c˘ au fost create de un
                         ¸      s    a       a           a
applet.


14.11       Appleturi care sunt ¸i aplicatii
                                s        ¸
Deoarece clasa Applet este derivat˘ din Container, deci ¸i din Component,
                                     a                       s
ea descrie o suprafat˘ de afi¸are care poate fi inclus˘ ca orice alt˘ component˘
                    ¸a      s                       a             a          a
ˆ
ıntr-un alt container, cum ar fi o fereastr˘. Un applet poate functiona ¸i ca
                                           a                          ¸   s
o aplicatie independent˘ astfel:
        ¸               a
422                                            CAPITOLUL 14. APPLETURI

    • Ad˘ug˘m metoda main clasei care descrie appletul, ˆ care vom face
        a a                                             ın
      operatiunile urm˘toare.
           ¸          a

    • Cre˘m o instant˘ a appletului ¸i o ad˘ug˘m pe suprafata unei ferestre.
         a          ¸a              s      a a             ¸

    • Apel˘m metodele init ¸i start, care ar fi fost apelate automat de
           a               s
      c˘tre browser.
       a

    • Facem fereastra vizibil˘.
                             a


                         Listing 14.8: Applet ¸i aplicatie
                                              s        ¸
import java . applet . Applet ;
import java . awt .*;

public class AppletAplicatie extends Applet                  {

    public void init () {
      add ( new Label ( " Applet si aplicatie " ) ) ;
    }

    public static void main ( String args []) {
      AppletAplicatie applet = new AppletAplicatie () ;
      Frame f = new Frame ( " Applet si aplicatie " ) ;
      f . setSize (200 , 200) ;

        f . add ( applet , BorderLayout . CENTER ) ;
        applet . init () ;
        applet . start () ;

        f . show () ;
    }
}
Capitolul 15

Lucrul cu baze de date

15.1       Introducere
15.1.1      Generalit˘¸i despre baze de date
                     at
Aplicatiile care folosesc baze de date sunt, ˆ general, aplicatii complexe
       ¸                                       ın                ¸
folosite pentru gestionarea unor informatii de dimensiuni mari ˆ
                                        ¸                      ıntr-o manier˘
                                                                            a
sigur˘ ¸i eficient˘.
      as         a


    Ce este o baz˘ de date ?
                      a
La nivelul cel mai general, o baz˘ de date reprezint˘ o modalitate de stocare a
                                  a                    a
unor informatii (date) pe un suport extern, cu posibilitatea reg˘sirii acestora.
                ¸                                                   a
Uzual, o baz˘ de date este memorat˘ ˆ
                a                       a ıntr-unul sau mai multe fi¸iere.
                                                                      s
    Modelul clasic de baze de date este cel relational, ˆ care datele sunt
                                                      ¸       ın
memorate ˆ tabele. Un tabel reprezint˘ o structur˘ de date format˘ dintr-o
             ın                            a             a                a
multime de articole, fiecare articol avˆnd definite o serie de atribute - aceste
    ¸                                    a
atribute corespund coloanelor tabelului, ˆ timp ce o linie va reprezenta un
                                             ın
articol. Pe lˆnga tabele, o baz˘ de date mai poate contine: proceduri ¸i
                a                  a                            ¸                 s
functii, utilizatori ¸i grupuri de utilizatori, tipuri de date, obiecte, etc.
     ¸               s
    Dintre produc˘torii cei mai importanti de baze de date amintim com-
                    a                         ¸
paniile Oracle, Sybase, IBM, Informix, Microsoft, etc. fiecare furnizˆnd o     a
serie ˆıntreag˘ de produse ¸i utilitare pentru lucrul cu baze de date. Aceste
               a             s
produse sunt ˆ general referite prin termenii DBMS (Database Management
                 ın
System) sau, ˆ traducere, SGBD (Sistem de Gestiune a Bazelor de Date). In
                 ın
acest capitol vom analiza lucrul cu baze de date din perspectiva program˘rii    a

                                       423
424                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE

ˆ limbajul Java, f˘r˘ a descrie particularit˘¸i ale unei solutii de stocare a
ın                 aa                       at               ¸
datelor anume. Vom vedea c˘, folosind Java, putem crea aplicatii care s˘
                              a                                    ¸        a
ruleze f˘r˘ nici o modificare folosind diverse tipuri de baze care au aceea¸i
        aa                                                                  s
structur˘, ducˆnd ˆ felul acesta notiunea de portabilitate ¸i mai departe.
        a      a ın                 ¸                      s


    Crearea unei baze de date
Crearea unei baze de date se face uzual folosind aplicatii specializate oferite
                                                       ¸
de produc˘torul tipului respectiv de sistem de gestiune a datelor, dar exist˘
           a                                                                  a
¸i posibilitatea de a crea o baza folosind un script SQL. Acest aspect ne va
s
preocupa ˆ a mai putin, exemplele prezentate presupunˆnd c˘ baza a fost
           ıns˘        ¸                                   a    a
creat˘ deja ¸i are o anumit˘ structur˘ specificat˘.
     a       s              a         a           a


   Accesul la baza de date
Se face prin intermediul unui driver specific tipului respectiv de SGBD.
Acesta este responsabil cu accesul efectiv la datele stocate, fiind legatura
dintre aplicatie ¸i baza de date.
             ¸ s


   Limbajul SQL
SQL (Structured Query Language) reprezint˘ un limaj de programare ce
                                               a
permite interogarea ¸i actualizarea informatiilor din baze de date relationale.
                      s                      ¸                         ¸
Acesta este standardizat astfel ˆ at diverse tipuri de drivere s˘ se comporte
                                 ıncˆ                           a
identic, oferind astfel o modalitate unitar˘ de lucru cu baze de date.
                                           a




15.1.2     JDBC
JDBC (Java Database Connectivity) este o interfat˘ standard SQL de acces
                                                ¸a
la baze de date. JDBC este constituit˘ dintr-un set de clase ¸i interfete
                                     a                        s        ¸
˘
15.2. CONECTAREA LA O BAZA DE DATE                                          425

scrise ˆ Java, furnizˆnd mecanisme standard pentru proiectantii aplicatiilor
       ın            a                                           ¸       ¸
ce folosesc de baze de date.
    Folosind JDBC este u¸or s˘ transmitem secvente SQL c˘tre baze de date
                           s   a                     ¸       a
relationale. Cu alte cuvinte, nu este necesar s˘ scriem un program pentru a
    ¸                                           a
accesa o baz˘ de date Oracle, alt program pentru a accesa o baz˘ de date
               a                                                    a
Sybase ¸i asa mai departe. Este de ajuns s˘ scriem un singur program folosind
         s                                a
API-ul JDBC ¸i acesta va fi capabil s˘ comunice cu drivere diferite, trimitˆnd
                 s                    a                                  ¸a
secvente SQL c˘tre baza de date dorit˘. Bineˆ ¸eles, scriind codul surs˘ ˆ
       ¸           a                    a        ınt                      a ın
Java, ne este asigurat˘ portabilitatea programului. Deci, iat˘ dou˘ motive
                       a                                        a    a
puternice care fac combinatia Java - JDBC demn˘ de luat ˆ seam˘.
                             ¸                       a       ın     a
    Pachetele care ofer˘ suport pentru lucrul cu baze de date sunt java.sql
                        a
ce reprezint˘ nucleul tehnologiei JDBC ¸i, preluat de pe platforma J2EE,
              a                           s
javax.sql.
    In linii mari, API-ul JDBC ofer˘ urm˘toarele facilit˘¸i:
                                    a     a              at

  1. Stabilirea unei conexiuni cu o baz˘ de date.
                                       a

  2. Efectuarea de secvente SQL.
                         ¸

  3. Prelucrarea rezultatelor obtinute.
                                ¸


15.2      Conectarea la o baz˘ de date
                             a
Procesul de conectare la o baz˘ de date implic˘ efectuarea a dou˘ operatii:
                              a               a                 a      ¸

  1. Inregistrarea unui driver corespunz˘tor.
                                        a

  2. Realizarea unei conexiuni propriu-zise.


   Definitie
          ¸
   O conexiune (sesiune) la o baz˘ de date reprezint˘ un context prin care
                                 a                   a
sunt trimise secvente SQL ¸i primite rezultate. Intr-o aplicatie pot exista
                   ¸       s                                  ¸
simultan mai multe conexiuni la baze de date diferite sau la aceea¸i baz˘.
                                                                  s     a

   Clasele ¸i interfetele responsabile cu realizarea unei conexiuni sunt:
           s         ¸

   • DriverManager - este clasa ce se ocup˘ cu ˆ
                                          a    ınregistrarea driverelor ce
     vor fi folosite ˆ aplicatie;
                    ın      ¸
426                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE

   • Driver - interfata pe care trebuie s˘ o implementeze orice clas˘ ce
                      ¸                  a                          a
     descrie un driver;
   • DriverPropertyInfo - prin intermediul acestei clase pot fi specificate
     diverse propriet˘¸i ce vor fi folosite la realizarea conexiunilor;
                     at
   • Connection - descrie obiectele ce modeleaz˘ o conexiune propriu-zis˘
                                               a                        a
     cu baza de date.

15.2.1     Inregistrarea unui driver
Primul lucru pe care trebuie s˘-l fac˘ o aplicatie ˆ procesul de conectare
                                a      a        ¸ ın
la o baz˘ de date este s˘ ˆ
        a               a ınregistreze la ma¸ina virtual˘ ce ruleaz˘ aplicatia
                                            s           a          a       ¸
driverul JDBC responsabil cu comunicarea cu respectiva baz˘ de date. Acest
                                                             a
lucru presupune ˆ arcarea ˆ memorie a clasei ce implementeaz˘ driver-ul ¸i
                 ınc˘       ın                                   a           s
poate fi realizat˘ ˆ mai multe modalit˘¸i.
                a ın                     at
  a. Folosirea clasei DriverManager:
     DriverManager.registerDriver(new TipDriver());
  b. Folosirea metodei Class.forName ce apeleaz˘ ClassLoader-ul ma¸inii
                                               a                  s
     virtuale:
     Class.forName("TipDriver");
     Class.forName("TipDriver").newInstance();
   c. Setarea propriet˘¸ii sistem jdbc.drivers, care poate fi realizat˘ ˆ dou˘
                      at                                             a ın   a
      feluri:
         – De la linia de comand˘:
                                a
           java -Djdbc.drivers=TipDriver Aplicatie
         – Din program:
           System.setProperty("jdbc.drivers", "TipDriver");
      Folosind aceast˘ metod˘, specificarea mai multor drivere se face separˆnd
                     a      a                                              a
      numele claselor cu punct ¸i virgul˘.
                               s        a
    Dac˘ sunt ˆ
       a       ınregistrate mai multe drivere, ordinea de precedent˘ ˆ alegerea
                                                                  ¸a ın
driverului folosit la crearea unei noi conexiuni este:
  1) Driverele ˆ
               ınregistrate folosind proprietatea jdbc.drivers la initializarea
                                                                     ¸
     ma¸inii virtuale ce va rula procesul.
        s
  2) Driverele ˆ
               ınregistrate dinamic din aplicatie.
                                              ¸
˘
15.2. CONECTAREA LA O BAZA DE DATE                                            427

15.2.2      Specificarea unei baze de date
O dat˘ ce un driver JDBC a fost ˆ
      a                          ınregistrat, acesta poate fi folosit la stabilirea
unei conexiuni cu o baz˘ de date. Avˆnd ˆ vedere faptul ca pot exista mai
                         a             a ın
multe drivere ˆ arcate ˆ memorie, trebuie s˘ avem posibilitea de a specifica
               ınc˘      ın                    a
pe lˆng˘ un identificator al bazei de date ¸i driverul ce trebuie folosit. Aceasta
    a a                                   s
se realizeaz˘ prin intermediul unei adrese specifice, numit˘ JDBC URL, ce
            a                                                a
are urm˘torul format:
         a

                      jdbc:sub-protocol:identificator

    Cˆmpul sub-protocol denume¸te tipul de driver ce trebuie folosit pentru
      a                           s
realizarea conexiunii ¸i poate fi odbc, oracle, sybase, db2 ¸i a¸a mai departe.
                      s                                    s s
    Identificatorul bazei de date este un indicator specific fiec˘rui driver core-
                                                               a
spunz˘tor bazei de date cu care aplicatia dore¸te s˘ interactioneze. In functie
       a                               ¸       s a          ¸               ¸
de tipul driver-ului acest identificator poate include numele unei ma¸ini   s
gazd˘, un num˘r de port, numele unui fi¸ier sau al unui director, etc., ca
     a          a                           s
ˆ exemplele de mai jos:
ın

  jdbc:odbc:test
  jdbc:oracle:thin@persistentjava.com:1521:test
  jdbc:sybase:test
  jdbc:db2:test

   Subprotocolul odbc este un caz specical, ˆ sensul c˘ permite specificarea
                                            ın        a
ˆ cadrul URL-ului a unor atribute ce vor fi realizate la crearea unei conexi-
ın
uni. Sintaxa completa subprotocolului odbc este:

                  jdbc:odbc:identificator[;atribut=valoare]*

  jdbc:odbc:test
  jdbc:odbc:test;CacheSize=20;ExtensionCase=LOWER
  jdbc:odbc:test;UID=duke;PWD=java

    La primirea unui JDBC URL, DriverManager-ul va parcurge lista driverelor
ˆ
ınregistrate ˆ memorie, pˆna cˆnd unul dintre ele va recunoa¸te URL-ul re-
             ın          a     a                              s
spectiv. Dac˘ nu exista nici unul potrivit, atunci va fi lansata o exceptie de
              a                                                        ¸
tipul SQLException, cu mesajul "no suitable driver".
428                       CAPITOLUL 15. LUCRUL CU BAZE DE DATE

15.2.3      Tipuri de drivere
Tipurile de drivere existente ce pot fi folosite pentru realizarea unei conexiuni
prin intermediul JDBC se ˆ  ımpart ˆ urm˘toarele categorii:
                                    ın      a


   Tip 1. JDBC-ODBC Bridge




    Acest tip de driver permite conectarea la o baz˘ de date care a fost
                                                         a
ˆ
ınregistrat˘ ˆ prealabil ˆ ODBC. ODBC (Open Database Conectivity)
            a ın           ın
reprezint˘ o modalitate de a uniformiza accesul la baze de date, asociind
          a
acestora un identificator DSN (Data Source Name) ¸i diver¸i parametri nece-
                                                       s      s
sari conect˘rii. Conectarea efectiv˘ la baza de date se va face prin intermediul
            a                       a
acestui identificator, driver-ul ODBC efectuˆnd comunicarea cu driverul na-
                                              a
tiv al bazei de date.
    De¸i simplu de utilizat, solutia JDBC-ODBC nu este portabil˘ ¸i comuni-
       s                         ¸                                  as
carea cu baza de date sufer˘ la nivelul vitezei de executie datorit˘ multiplelor
                            a                            ¸         a
redirect˘ri ˆ
         a ıntre drivere. De asemenea, atˆt ODBC-ul cˆt ¸i driver-ul nativ
                                            a               a s
trebuie s˘ existe pe ma¸ina pe care ruleaz˘ aplicatia.
          a              s                  a        ¸
    Clasa Java care descrie acest tip de driver JDBC este:

                     sun.jdbc.odbc.JdbcOdbcDriver

¸i este inclus˘ ˆ distributia standard J2SDK. Specificarea bazei de date se
s             a ın        ¸
face printr-un URL de forma:

                          jdbc:odbc:identificator

unde identif icator este profilul (DSN) creat bazei de date ˆ ODBC.
                                                           ın


   Tip 2. Driver JDBC - Driver nativ
˘
15.2. CONECTAREA LA O BAZA DE DATE                                          429




    Acest tip de driver transform˘ cererile JDBC direct ˆ apeluri c˘tre
                                    a                        ın           a
driverul nativ al bazei de date, care trebuie instalat ˆ prealabil. Clase Java
                                                       ın
care implementeaz˘ astfel de drivere pot fi procurate de la produc˘torii de
                   a                                                  a
SGBD-uri, distributia standard J2SDK neincluzˆnd nici unul.
                    ¸                             a


   Tip 3. Driver JDBC - Server




    Acest tip de driver transform˘ cererile JDBC folosind un protocol de retea
                                   a                                         ¸
independent, acestea fiind apoi transormate folosind o aplicatie server ˆ
                                                               ¸         ıntr-un
protocol specfic bazei de date. Introducerea serverului ca nivel intermediar
aduce flexibilitate maxim˘ ˆ sensul c˘ vor putea fi realizate conexiuni cu
                            a ın         a
diferite tipuri de baze, f˘r˘ nici o modificare la nivelul clientului. Protocolul
                          aa
folosit este specific fiec˘rui produc˘tor.
                         a           a


   Tip 4. Driver JDBC nativ
430                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE




   Acest tip de driver transform˘ cererile JDBC direct ˆ cereri c˘tre baza
                                  a                       ın         a
de date folosind protocolul de retea al acesteia. Aceast˘ solutie este cea mai
                                 ¸                      a     ¸
rapid˘, fiind preferat˘ la dezvoltarea aplicatiilor care manevreaz˘ volume
     a                a                       ¸                      a
mari de date ¸i viteza de executie este critic˘. Drivere de acest tip pot fi
               s                 ¸             a
procurate de la diver¸i produc˘tori de SGBD-uri.
                     s         a

15.2.4     Realizarea unei conexiuni
Metoda folosit˘ pentru realizarea unei conexiuni este getConnection din
              a
clasa DriverManager ¸i poate avea mai multe forme:
                     s

  Connection c = DriverManager.getConnection(url);
  Connection c = DriverManager.getConnection(url, username, password);
  Connection c = DriverManager.getConnection(url, dbproperties);

   Stabilirea unei conexiuni folosind driverul JDBC-ODBC

  String url = "jdbc:odbc:test" ;
  // sau url = "jdbc:odbc:test;UID=duke;PWD=java" ;
  try {
    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
  } catch(ClassNotFoundException e) {
    System.err.print("ClassNotFoundException: " + e) ;
    return ;
  }

  Connection con ;
  try {
    con = DriverManager.getConnection(url, "duke", "java");
  } catch(SQLException e) {
    System.err.println("SQLException: " + e);
15.3. EFECTUAREA DE SECVENTE SQL
                          ¸                                               431

  } finally {
    try{
      con.close ;
    } catch(SQLException e) {
      System.err.println(SQLException: " + e) ;
    }
  }
    Stabilirea unei conexiuni folosind un driver MySql
Folosirea diferitelor tipuri de drivere implic˘ doar schimbarea numelui clasei
                                              a
ce reprezint˘ driverul ¸i a modalit˘¸ii de specificare a bazei de date.
            a           s            at
  String url = "jdbc:mysql://localhost/test" ;
  // sau url = "jdbc:mysql://localhost/test?user=duke&password=java";
  try {
    Class.forName("com.mysql.jdbc.Driver") ;
  } catch(ClassNotFoundException e) {
  ...
   O conexiune va fi folosit˘ pentru:
                           a
   • Crearea de secvente SQL utilizate pentru interogarea sau actualizarea
                      ¸
     bazei.

   • Aflarea unor informatii legate de baza de date (meta-date).
                        ¸
   De asemenea, clasa Connection asigur˘ facilit˘¸i pentru controlul tranzactiilor
                                         a      at                          ¸
din memorie c˘tre baza de date prin metodele commit, rollback, setAu-
              a
toCommit.
   Inchiderea unei conexiuni se realizeaz˘ prin metoda close.
                                         a


15.3      Efectuarea de secvente SQL
                              ¸
O dat˘ facut˘ conectarea cu metoda DriverManager.getConection, se poate
      a      a
folosi obiectul Connection rezultat pentru a se crea obiecte de tip State-
ment,PreparedStatement sau CallableStatement cu ajutorul c˘rora      a
putem trimite secvente SQL c˘tre baza de date. Cele mai uzuale comenzi
                      ¸        a
SQL sunt cele folosite pentru:
   • Interogarea bazei de date: SELECT
432                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE

   • Actualizarea datelor: INSERT, UPDATE, DELETE

   • Actualizarea structurii: CREATE, ALTER, DROP - acestea mai sunt nu-
     mite instructiuni DDL (Data Definition Language)
                 ¸

   • Apelarea unei proceduri stocate: CALL

    Dup˘ cum vom vedea, obtinerea ¸i prelucrarea rezultatelor unei interog˘ri
        a                    ¸       s                                    a
este realizat˘ prin intermediul obiectelor de tip ResultSet.
             a

15.3.1     Interfata Statement
                  ¸
Interfata Statement ofer˘ metodele de baz˘ pentru trimiterea de secvente
       ¸                a                a                               ¸
SQL c˘tre baza de date ¸i obtinerea rezultatelor, celelalte dou˘ interfete:
       a                 s   ¸                                 a        ¸
PreparedStatement ¸i CallableStatement fiind derivate din aceasta.
                    s
    Crearea unui obiect Statement se realizeaz˘ prin intermediul metodei
                                              a
createStatement a clasei Connection, f˘r˘ nici un argument:
                                       aa

  Connection con = DriverManager.getConnection(url);
  Statement stmt = con.createStatement();

   Executia unei secvente SQL poate fi realizat˘ prin intermediul a trei
        ¸              ¸                      a
metode:


    1. executeQuery
Este folosit˘ pentru realizarea de interog˘ri de tip SELECT. Metoda returneaz˘
             a                            a                                  a
un obiect de tip ResultSet ce va contine sub o form˘ tabelar˘ rezultatul
                                         ¸                a       a
interog˘rii.
       a

  String sql = "SELECT * FROM persoane";
  ResultSet rs = stmt.executeQuery(sql);



    2. executeUpdate
Este folosit˘ pentru actualizarea datelor (INSERT, UPDATE, DELETE) sau
            a
a structurii bazei de date (CREATE, ALTER, DROP). Metoda va returna un
ˆ
ıntreg ce semnific˘ num˘rul de linii afectate de operatiunea de actualizare a
                  a     a                            ¸
datelor, sau 0 ˆ cazul unei instructiuni DDL.
               ın                  ¸
15.3. EFECTUAREA DE SECVENTE SQL
                          ¸                                                 433

  String sql = "DELETE FROM persoane WHERE cod > 100";
  int linii = stmt.executeUpdate(sql);
  // Nr de articole care au fost afectate (sterse)

  sql = "DROP TABLE temp";
  stmt.executeUpdate(sql); // returneaza 0



     3. execute
Aceast˘ metod˘ va fi folosit˘ doar dacˆ este posibil ca rezultatul unei in-
        a       a              a          a
terog˘ri s˘ fie format din dou˘ sau mai multe obiecte de tip ResultSet sau
      a a                       a
rezultatul unei actualiz˘ri s˘ fie format din mai mule valori, sau o combinatie
                        a a                                                   ¸
ˆ
ıntre aceste cazuri. Aceast˘ situatie, de¸i mai rar˘, este posibil˘ atunci cˆnd
                            a       ¸    s         a              a          a
sunt executate proceduri stocate sau secvente SQL cunoscute abia la momen-
                                              ¸
tul executiei, programatorul ne¸tiind deci dac˘ va fi vorba de o actualizare
          ¸                       s             a
a datelor sau a structurii. Metoda ˆ   ıntoarce true dac˘ rezultatul obtinut
                                                           a               ¸
este format din obiecte de tip ResultSet ¸i false dac˘ e format din ˆ
                                            s            a              ıntregi.
In functie de aceasta, pot fi apelate metodele: getResultSet sau getUp-
        ¸
dateCount pentru a afla efectiv rezultatul comenzii SQL. Pentru a prelua
toate rezultatele va fi apelat˘ metoda getMoreResults, dup˘ care vor fi
                               a                                   a
apelate din nou metodele amintite, pˆn˘ la obtinerea valorii null, respectiv
                                       a a       ¸
−1. Secventa complet˘ de tratare a metodei execute este prezentat˘ mai
             ¸          a                                                 a
jos:
  String sql = "comanda SQL necunoscuta";
  stmt.execute(sql);
  while(true) {
    int rowCount = stmt.getUpdateCount();
    if(rowCount > 0) {
      // Este o actualizare datelor
      System.out.println("Linii afectate = " + rowCount);
      stmt.getMoreResults();
      continue;
    }

     if(rowCount = 0) {
       // Comanda DDL sau nici o linie afectata
       System.out.println("Comanda DDL sau 0 actualizari");
434                     CAPITOLUL 15. LUCRUL CU BAZE DE DATE

          stmt.getMoreResults();
          continue;
      }

      // rowCount este -1
      // Avem unul sau mai multe ResultSet-uri
      ResultSet rs = stmt.getResultSet();
      if(rs != null) {
        // Proceseaza rezultatul
        ...
        stmt.getMoreResults();
        continue;
      }

      // Nu mai avem nici un rezultat
      break;
  }

    Folosind clasa Statement, ˆ cazul ˆ care dorim s˘ introducem valorile
                               ın      ın              a
unor variabile ˆ
               ıntr-o secvent˘ SQL, nu avem alt˘ solutie decˆt s˘ cre˘m un
                            ¸a                  a      ¸      a a      a
¸ir de caractere compus din instructiuni SQL ¸i valorile variabilelor:
s                                  ¸         s

  int cod = 100;
  String nume = "Popescu";
  String sql = "SELECT * FROM persoane WHERE cod=" + cod +
   " OR nume=’" + nume + "’";
  ResultSet rs = stmt.executeQuery(sql);

15.3.2       Interfata PreparedStatement
                    ¸
Interfata PreparedStatement este derivat˘ din Statement, fiind diferit˘ de
       ¸                                a                            a
aceasta ˆ urm˘toarele privinte:
         ın   a             ¸

   • Instantele de tip PreparedStatement contin secvente SQL care au fost
           ¸                                ¸         ¸
     deja compilate (sunt ”preg˘tite”).
                               a

   • O secvent˘ SQL specificat˘ unui obiect PreparedStatement poate s˘
              ¸a               a                                         a
     aib˘ unul sau mai multi parametri de intrare, care vor fi specificati
        a                    ¸                                          ¸
     prin intermediul unui semn de ˆ
                                   ıntrebare (”?”) ˆ locul fiec˘ruia dintre
                                                   ın         a
15.3. EFECTUAREA DE SECVENTE SQL
                          ¸                                                435

     ei. Inainte ca secventa SQL s˘ poat˘ fi executat˘ fiec˘rui parametru
                           ¸         a     a             a    a
     de intrare trebuie s˘ i se atribuie o valoare, folosind metode specifice
                         a
     acestei clase.
    Executia repetat˘ a aceleia¸i secvente SQL, dar cu parametri diferiti, va
          ¸          a         s        ¸                              ¸
fi ˆ general mai rapid˘ dac˘ folosim PreparedStatement, deoarece nu mai
  ın                   a     a
trebuie s˘ cre˘m cˆte un obiect de tip Statement pentru fiecare apel SQL, ci
         a    a    a
refolosim o singur˘ instant˘ precompilat˘ furnizˆndu-i doar alte argumente.
                  a       ¸a              a     a
    Crearea unui obiect de tip PreparedStatement se realizeaz˘ prin inter-
                                                                a
mediul metodei prepareStatement a clasei Connection, specificˆn ca ar-
                                                                   a
gument o secvent˘ SQL ce contine c˘te un semn de ˆ
                 ¸a            ¸     a              ıntrebare pentru fiecare
parametru de intrare:
  Connection con = DriverManager.getConnection(url);
  String sql = "UPDATE persoane SET nume=? WHERE cod=?";
  Statement pstmt = con.prepareStatement(sql);
    Obiectul va pstmt contine o comand˘ SQL precompilat˘ care este trimis˘
                          ¸             a                a                 a
imediat c˘tre baza de date, unde va a¸tepta parametri de intrare pentru a
          a                            s
putea fi executat˘.a
    Trimiterea parametrilor se realizeaz˘ prin metode de tip setXXX,
                                           a
unde XXX este tipul corespunz˘tor parametrului, iar argumentele metodei
                                 a
sunt num˘rul de ordine al parametrului de intrare (al semnului de ˆ
          a                                                       ıntrebare)
¸i valoarea pe care dorim s˘ o atribuim.
s                           a
  pstmt.setString(1, "Ionescu");
  pstmt.setInt(2, 100);
    Dup˘ stabilirea parametrilor de intrare secventa SQL poate fi executat˘.
         a                                             ¸                      a
Putem apoi stabili alte valori de intrare ¸i refolosi obiectul PreparedStatement
                                          s
pentru executii repetate ale comenzii SQL. Este ˆ a posibil ca SGBD-ul
              ¸                                          ıns˘
folosit s˘ nu suporte acest tip de operatiune ¸i s˘ nu retin˘ obiectul pre-
         a                                   ¸      s a         ¸ a
compilat pentru executii ulterioare. In aceast˘ situatie folosirea interfetei
                        ¸                           a        ¸               ¸
PreparedStatement ˆ loc de Statement nu va ˆ
                      ın                                ımbun˘t˘¸i ˆ nici un fel
                                                               a at ın
performanta codului, din punctul de vedere al vitezei de executie a acestuia.
            ¸                                                       ¸
    Executia unei secvente SQL folosind un obiect PreparedStatement se
            ¸              ¸
realizeaz˘ printr-una din metodele executeQuery, executeUpdate sau
          a
execute, semnificatiile lor fiind acelea¸i ca ¸i ˆ cazul obiectelor de tip
                    ¸                       s      s ın
Statement, cu singura deosebire c˘ ˆ cazul de fat˘ ele nu au nici un ar-
                                      a ın                ¸a
gument.
436                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE

  String sql = "UPDATE persoane SET nume=? WHERE cod=?";
  Statement pstmt = con.prepareStatement(sql);
  pstmt.setString(1, "Ionescu");
  pstmt.setInt(2, 100);
  pstmt.executeUpdate();

  pstmt.setString(1, "Popescu");
  pstmt.setInt(2, 200);
  pstmt.executeUpdate();

  sql = "SELECT * from persoane WHERE cod >= ?";
  pstmt = con.prepareStatement(sql);
  pstmt.setInt(1, 100);
  ResultSet rs = pstmt.executeQuery();
   Fiec˘rui tip Java ˆ corespunde un tip generic SQL. Este responsabilitatea
         a             ıi
programatorului s˘ se asigure c˘ folose¸te metoda adecvat˘ de tip setXXX la
                    a              a      s                 a
stabilirea valorii unui parametru de intrare. Lista tuturor tipurilor generice
disponibile, numite ¸i tipuri JDBC, este definit˘ de clasa Types, prin con-
                      s                           a
stantelor declarate de aceasta. Metoda setObject permite specificarea unor
valori pentru parametrii de intrare, atunci cˆnd dorim s˘ folosim maparea
                                               a           a
implicit˘ ˆ
         a ıntre tipurile Java ¸i cele JDBC sau atunci cˆnd dorim s˘ preciz˘m
                               s                        a          a       a
explicit un tip JDBC.
  pstmt.setObject(1, "Ionescu", Types.CHAR);
  pstmt.setObject(2, 100, Types.INTEGER); // sau doar
  pstmt.setObject(2, 100);
   Folosind metoda setNull putem s˘ atribuim unui parametru de intrare
                                      a
valoare SQL NULL, trebuind ˆ a s˘ specific˘m ¸i tipul de date al coloanei ˆ
                            ıns˘ a         a s                           ın
care vom scrie aceast˘ valoare. Acela¸i lucru poate fi realizat cu metode de
                     a               s
tipul setXXX dac˘ argumentul folosit are valoarea null.
                 a
  pstmt.setNull(1, Types.CHAR);
  pstmt.setInt(2, null);
   Cu ajutorul metodelor setBytes sau setString avem posibilitatea de a
specifica date de orice dimensiuni ca valori pentru anumite articole din baza
de date. Exist˘ ˆ a situatii cˆnd este de preferat ca datele de mari dimensi-
              a ıns˘      ¸ a
uni s˘ fie transferate pe ”buc˘¸i” de o anumit˘ dimensiune. Pentru a realiza
     a                        at              a
15.3. EFECTUAREA DE SECVENTE SQL
                          ¸                                               437

acest lucru API-ul JDBC pune la dispozitie metodele setBinaryStream,
                                             ¸
setAsciiStream ¸i setUnicodeStream care ata¸eaz˘ un flux de intrare pe
                   s                                s a
octeti, caractere ASCII, respectiv UNICODE, unui parametru de intrare. Pe
    ¸
m˘sur˘ ce sunt citite date de pe flux, ele vor fi atribuite parametrului. Exem-
  a a
plul de mai jos ilustreaz˘ acest lucru, atribuind coloanei continut continutul
                         a                                              ¸
unui anumit fi¸ier:
               s

  File file = new File("date.txt");
  int fileLength = file.length();
  InputStream fin = new FileInputStream(file);
  java.sql.PreparedStatement pstmt = con.prepareStatement(
    "UPDATE fisiere SET continut = ? WHERE nume = ’date.txt’");
  pstmt.setUnicodeStream (1, fin, fileLength);
  pstmt.executeUpdate();

    La executia secventei, fluxul de intrare va fi apelat repetat pentru a
              ¸         ¸
furniza datele ce vor fi scrise ˆ coloana continut a articolului specificat.
                                  ın
Observati c˘ este necesar ˆ a s˘ ¸tim dinainte dimensiunea datelor ce vor fi
         ¸ a                ın˘ a s
scrise, acest lucru fiind solicitat de unele tipuri de baze de date.

15.3.3     Interfata CallableStatement
                  ¸
Interfata CallableStatement este derivat˘ din PreparedStatement, instantele
       ¸                                  a                                ¸
de acest tip oferind o modalitate de a apela o procedur˘ stocat˘ ˆ
                                                       a       a ıntr-o baz˘
                                                                           a
de date, ˆ
         ıntr-o manier˘ standar pentru toate SGBD-urile.
                       a
    Crearea unui obiect CallableStatement se realizeaz˘ prin metoda pre-
                                                         a
pareCall a clasei Connection:

  Connection con = DriverManager.getConnection(url);
  CallableStatement cstmt = con.prepareCall(
    "{call proceduraStocata(?, ?)}");

    Trimiterea parametrilor de intrare se realizeaz˘ ˆa ıntocmai ca la PreparedStatement,
cu metode de tip setXXX. Dac˘ procedura are ¸i parametri de ie¸ire (valori
                                 a               s                   s
returnate), ace¸tia vor trebui ˆ
                s              ınregistrati cu metoda registerOutParame-
                                         ¸
ter ˆ
    ınainte de executia procedurii. Obtinerea valorilor rezultate ˆ parametrii
                     ¸                 ¸                          ın
de ie¸ie se va face cu metode de tip getXXX.
     s

  CallableStatement cstmt = con.prepareCall(
438                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE

    "{call calculMedie(?)}");
  cstmt.registerOutParameter(1, java.sql.Types.FLOAT);
  cstmt.executeQuery();
  float medie = cstmt.getDouble(1);

    Este posibil ca un parametru de intrare s˘ fie ¸i parametru de ie¸ire. In
                                             a    s                 s
acest caz el trebuie s˘ primeasc˘ o valoare cu setXXX ¸i, de asemenea, va fi
                      a         a                      s
ˆ
ınregistrat cu registerOutParameter, tipurile de date specificate trebuind
s˘ coincid˘.
  a       a

15.3.4     Obtinerea ¸i prelucrarea rezultatelor
             ¸       s
15.3.5     Interfata ResultSet
                  ¸
In urma executie unei interog˘ri SQL rezultatul va fi reprezentat printr-un
               ¸               a
obiect de tip ResultSet, ce va contine toate liniile ce satisfac conditiile
                                       ¸                                ¸
impuse de comanda SQL. Forma general˘ a unui ResultSet este tabelar˘,
                                         a                                a
avˆnd un num˘r de coloane ¸i de linii, functie de secventa executat˘. De
  a            a              s               ¸            ¸          a
asemenea, obiectul va contine ¸i meta-datele interog˘rii cum ar fi denumirele
                         ¸     s                    a
coloanelor selectate, num˘rul lor, etc.
                         a
  Statement stmt = con.createStatement();
  String sql = "SELECT cod, nume FROM persoane";
  ResultSet rs = stmt.executeQuery(sql);
Rezultatul interog˘rii de mai sus va fi obiectul rs cu urm˘toarea structur˘:
                  a                                      a               a
                               cod nume
                               100 Ionescu
                               200 Popescu
    Pentru a extrage informatiile din aceast˘ structur˘ va trebui s˘ parcurgem
                               ¸              a        a            a
tabelul linie cu linie ¸i din fiecare s˘ extragem valorile de pe coloane. Pentru
                       s              a
acest lucru vom folosi metode de tip getXXX, unde XXX este tipul de
dat˘ al unei coloane iar argumentul primit indic˘ fie num˘rul de ordine din
    a                                              a          a
cadrul tabelului, fie numele acestuia. Coloanele sunt numerotate de la stˆnga
                                                                           a
la dreapta, ˆ ıncepˆnd cu 1. In general, folosirea indexului coloanei ˆ loc de
                   a                                                   ın
numele s˘u va fi mai eficient˘. De asemenea, pentru maxim˘ portabilitate
          a                      a                               a
se recomand˘ citirea coloanelor ˆ ordine de la stˆnga la dreapta ¸i fiecare
               a                    ın               a                 s
citire s˘ se fac˘ o singur˘ dat˘.
        a        a         a     a
15.3. EFECTUAREA DE SECVENTE SQL
                          ¸                                                439

    Un obiect ResultSet folose¸te un cursor pentru a parcurge articolele
                                  s
rezultate ˆ urma unei interog˘ri. Initial acest cursor este pozitionat ˆ
          ın                   a      ¸                         ¸      ınaintea
primei linii, fiecare apel al metodei next determinˆnd trecerea la urm˘toarea
                                                   a                    a
linie. Deoarece next returneaz˘ false cˆnd nu mai sunt linii de adus, uzual
                                a        a
va fi folosit˘ o bucl˘ while-loop petru a itera prin articolele tabelului:
            a        a

  String sql = "SELECT cod, nume FROM persoane";
  ResultSet rs = stmt.executeQuery(sql);
  while (rs.next()) {
    int cod = r.getInt("cod");
    String nume = r.getString("nume");
    /* echivalent:
    int cod = r.getInt(1);
    String nume = r.getString(2);
    */
    System.out.println(cod + ", " + nume);
  }

    Implicit, un tabel de tip ResultSet nu poate fi modificat iar cursorul
asociat nu se deplaseaz˘ decˆt ˆ
                         a    a ınainte, linie cu linie. A¸adar, putem itera
                                                          s
prin rezultatul unei interog˘ri o singur˘ dat˘ ¸i numai de la prima la ultima
                            a           a    as
linie. Este ˆ a posibil s˘ cre˘m ResultSet-uri care s˘ permit˘ modificarea
            ıns˘         a    a                         a       a
sau deplasarea ˆ ambele sensuri. Exemplul urm˘tor va folosi un cursor care
                 ın                               a
este modificabil ¸i nu va reflecta schimb˘rile produse de alti utilizatori dup˘
                  s                       a                 ¸               a
crearea sa:
  Statement stmt = con.createStatement(
          ResultSet.TYPE_SCROLL_INSENSITIVE,
          ResultSet.CONCUR_UPDATABLE);
  String sql = "SELECT cod, nume FROM persoane";
  ResultSet rs = stmt.executeQuery(sql);
   Dac˘ un ResultSet folose¸te un cursor modificabil ¸i care poate naviga
        a                     s                          s
ˆ ambele sensuri, atunci are la dispozitie o serie de metode ce se bazeaz˘ pe
ın                                     ¸                                 a
acest suport:
   • absolute - Deplaseaz˘ cursorul la o anumit˘ linie specificat˘ absolut;
                         a                     a                a

   • updateXXX - Actualizeaz˘ valoarea unei coloane din linia curent˘,
                              a                                     a
     unde XXX este un tip de date.
440                      CAPITOLUL 15. LUCRUL CU BAZE DE DATE

   • updateRow - Transfer˘ actualiz˘rile f˘cute liniei ˆ baza de date.
                         a         a      a            ın

   • moveToInsertRow - deplaseaz˘ cursorul la o linie spceial˘, numit˘
                                       a                         a       a
     linie nou˘, utilizate˘ pentru a introduce noi articole ˆ baza de date.
               a          a                                 ın
     Linia curent˘ anterioar˘ a cursorului va fi memorat˘ pentru a se putea
                   a         a                           a
     reveni la ea.

   • insertRow - insereaz˘ articolul din zona linie nou˘ ˆ baza de date;
                             a                               a ın
     cursorul trebuie s˘ fie pozitionat le linia nou˘ la executia acestei operatiuni.
                       a        ¸                  a         ¸                ¸

   • moveToCurrentRow - revine la linia curent˘ din tabel.
                                              a

   • deleteRow - ¸terge linia curent˘ din tabel ¸i din baza de date; nu
                   s                  a          s
     poate fi apelat˘ cˆnd cursorul este ˆ modul linie nou˘.
                   a a                  ın               a

    Nu toate sistemele de gestiune a bazelor de date ofer˘ suport pentru
                                                           a
folosirea cursoarelor care pot fi modificate. Pentru a determina dac˘ baza de
                                                                  a
date permite acest lucru pot fi utilizate metodele supportsPositionedUp-
date ¸i supportsPositionedDelete ale clasei DatabaseMetaData. In cazul
       s
ˆ care acest lucru este permis, este responsabilitatea driver-ului bazei de
ın
date s˘ asigure rezolvarea problemelor legate de actualizarea concurent˘ a
        a                                                               a
unui cursor, astfel ˆ at s˘ nu apar˘ anomalii.
                    ıncˆ a          a

15.3.6     Exemplu simplu
In continuare vom da un exemplul simplu de utilizare a claselor de baz˘ a
mentionate anterior. Programul va folosi o baz˘ de date MySql, ce contine
    ¸                                         a                      ¸
un tabel numit persoane, avˆnd coloanele: cod, nume ¸i salariu. Scriptul
                            a                         s
SQL de creare a bazei este:

create table persoane(cod integer, nume char(50), salariu double);

  Aplicatia va goli tabelul cu persoane, dup˘ care va ad˘uga aleator un
        ¸                                       a            a
num˘r de articole, va efectua afi¸area lor ¸i calculul mediei salariilor.
   a                            s         s

              Listing 15.1: Exemplu simplu de utilzare JDBC
import java . sql .*;

public class TestJdbc {
  public static void main ( String [] args ) {
15.3. EFECTUAREA DE SECVENTE SQL
                          ¸                                              441

   String url = " jdbc : mysql :// localhost / test " ;
   try {
     Class . forName ( " com . mysql . jdbc . Driver " ) ;
   } catch ( Cl as sNo tF oun dE x c e p t i o n e ) {
     System . out . println ( " Eroare incarcare driver ! n " + e ) ;
     return ;
   }
   try {
     Connection con = DriverManager . getConnection ( url ) ;

     // Golim tabelul persoane
     String sql = " DELETE FROM persoane " ;
     Statement stmt = con . createStatement () ;
     stmt . executeUpdate ( sql ) ;

     // Adaugam un numar de persoane generate aleator
     // Tabelul persoane are coloanele ( cod , nume , salariu )
     int n = 10;
     sql = " INSERT INTO persoane VALUES (? , ? , ?) " ;
     PreparedStatement pstmt = con . prepareStatement ( sql ) ;
     for ( int i =0; i < n ; i ++) {
       int cod = i ;
       String nume = " Persoana " + i ;
       double salariu = 100 + Math . round ( Math . random () *
            900) ;
       // salariul va fi intre 100 si 1000
       pstmt . setInt (1 , cod ) ;
       pstmt . setString (2 , nume ) ;
       pstmt . setDouble (3 , salariu ) ;
       pstmt . executeUpdate () ;
     }

     // Afisam persoanele ordonate dupa salariu
     sql = " SELECT * FROM persoane ORDER BY salariu " ;
     ResultSet rs = stmt . executeQuery ( sql ) ;
     while ( rs . next () )
       System . out . println ( rs . getInt ( " cod " ) + " , " +
                                rs . getString ( " nume " ) + " , " +
                                rs . getDouble ( " salariu " ) ) ;


     // Calculam salariul mediu
     sql = " SELECT avg ( salariu ) FROM persoane " ;
     rs = stmt . executeQuery ( sql ) ;
     rs . next () ;
442                        CAPITOLUL 15. LUCRUL CU BAZE DE DATE

          System . out . println ( " Media : " + rs . getDouble (1) ) ;

          // Inchidem conexiunea
          con . close () ;

        } catch ( SQLException e ) {
          e . printStackTrace () ;
        }
    }
}




15.4         Lucrul cu meta-date
15.4.1       Interfata DatabaseMetaData
                    ¸
Dup˘ realizarea unui conexiuni la o baz˘ de date, putem apela metoda get-
     a                                   a
MetaData pentru a afla diverse informatii legate de baza respectiv˘, a¸a
                                           ¸                             a s
numitele meta-date (”date despre date”); Ca rezult al apelului metodei, vom
obtine un obiect de tip DatabaseMetaData ce ofer˘ metode pentru deter-
  ¸                                                    a
minarea tabelelor, procedurilor stocate, capabilit˘¸ilor conexiunii, gramaticii
                                                  at
SQL suportate, etc. ale bazei de date.
    Programul urm˘tor afi¸eaz˘ numele tuturor tabelelor dintr-o baz˘ de dat
                   a     s a                                           a
ˆ
ınregistrat˘ ˆ ODBC.
           a ın

              Listing 15.2: Folosirea interfetei DatabaseMetaData
                                             ¸
import java . sql .*;

public class TestMetaData {
  public static void main ( String [] args ) {
    String url = " jdbc : odbc : test " ;
    try {
      Class . forName ( " sun . jdbc . odbc . JdbcOdbcDriver " ) ;
    } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) {
      System . out . println ( " Eroare incarcare driver ! n " + e ) ;
      return ;
    }
    try {
      Connection con = DriverManager . getConnection ( url ) ;
      DatabaseMetaData dbmd = con . getMetaData () ;
      ResultSet rs = dbmd . getTables ( null , null , null , null ) ;
15.4. LUCRUL CU META-DATE                                                      443

          while ( rs . next () )
              System . out . println ( rs . getString ( " TABLE_NAME " ) ) ;
          con . close () ;
        } catch ( SQLException e ) {
          e . printStackTrace () ;
        }
    }
}



15.4.2        Interfata ResultSetMetaData
                     ¸
Meta-datele unui ResultSet reprezint˘ informatiile despre rezultatul continut
                                        a         ¸                        ¸
ˆ acel obiect cum ar fi num˘rul coloanelor, tipul ¸i denumirile lor, etc. Aces-
ın                          a                     s
tea sunt obtinute apelˆnd metoda getMetaData pentru ResultSet-ul re-
             ¸         a
spectiv, care va returna un obiect de tip ResultSetMetaData ce poate fi
apoi folosit pentru extragerea informatiilor dorite.
                                      ¸

    ResultSet rs = stmt.executeQuery("SELECT * FROM tabel");
    ResultSetMetaData rsmd = rs.getMetaData();
    // Aflam numarul de coloane
    int n = rsmd.getColumnCount();

    // Aflam numele coloanelor
    Sring nume[] = new String[n+1];
    for(int i=1; i<=n; i++)
      nume[i] = rsmd.getColumnName(i);
444   CAPITOLUL 15. LUCRUL CU BAZE DE DATE
Capitolul 16

Lucrul dinamic cu clase

16.1      Inc˘rcarea claselor ˆ memorie
             a                ın
Dup˘ cum ¸tim executia unei aplicatii Java este realizat˘ de c˘tre ma¸ina
     a      s           ¸               ¸                   a     a        s
virtual˘ Java (JVM), aceasta fiind responsabil˘ cu interpretarea codului de
       a                                         a
octeti rezultat ˆ urma compil˘rii. Spre deosebire de alte limbaje de progra-
     ¸          ın              a
mare cum ar fi C sau C++, un program Java compilat nu este descris de un
fi¸ier executabil ci de o multime de fi¸iere cu extensia .class corespunz˘toare
 s                          ¸         s                                  a
fiec˘rei clase a programului. In plus, aceste clase nu sunt ˆ arcate toate ˆ
    a                                                         ın˘            ın
memorie la pornirea aplicatiei, ci sunt ˆ arcate pe parcursul executie acesteia
                            ¸             ın˘                      ¸
atunci cˆnd este nevoie de ele, momentul efectiv ˆ care se realizeaz˘ acest
         a                                          ın                  a
lucru depinzˆnd de implementarea ma¸inii virtuale.
             a                             s
    Ciclul de viat˘ al unei clase are a¸adar urm˘toarele etape:
                 ¸a                     s        a

  1. Inc˘rcarea - Este procesul reg˘sirii reprezent˘rii binare a unei clase
         a                          a               a
     (fi¸ierul .class) pe baza numelui complet al acestuia ¸i ˆ arcarea aces-
       s                                                  s ınc˘
     teia ˆ memorie. In urma acestui proces, va fi instantiat un obiect de
           ın                                              ¸
     tip java.lang.Class, corespunz˘tor clasei respective. Operatiunea de
                                     a                            ¸
     ˆ arcare a unei clase este realizat˘ la un moment ce precede prima
     ınc˘                               a
     utilizare efectiv˘ a sa.
                      a

  2. Editarea de leg˘turi - Specific˘ incorporarea noului tip de date ˆ JVM
                    a              a                                 ın
     pentru a putea fi utlizat.

  3. Initializarea - Const˘ ˆ executia blocurilor statice de initializare ¸i
        ¸                   a ın       ¸                        ¸         s
     initializarea variabilelor de clas˘.
        ¸                              a

                                     445
446                      CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

   4. Desc˘rcarea - Atunci cˆnd nu mai exist˘ nici o referint˘ de tipul clasei
           a                  a                a            ¸a
      respective, obiectul de tip Class creat va fi marcat pentru a fi eliminat
      din memorie de c˘tre garbage collector.
                        a

   Inc˘rcarea claselor unei aplicatii Java ˆ memorie este realizat˘ prin in-
      a                           ¸        ın                     a
termediul unor obiecte pe care le vom numi generic class loader. Acestea
sunt de dou˘ tipuri:
           a

   1. Class loader-ul primordial (eng. bootstrap) - Reprezint˘ o parte inte-
                                                             a
      grant˘ a ma¸inii virtuale, fiind responsabil cu ˆ arcarea claselor stan-
           a      s                                  ınc˘
      dard din distributia Java.
                       ¸

   2. Class loader-e proprii - Acestea nu fac parte intrinsec˘ din JVM ¸i
                                                                a             s
      sunt instante ale clasei java.lang.ClassLoader. Aceasta este o clas˘
                 ¸                                                            a
      abstract˘, tipul efectiv al obiectului fiind a¸adar derivat din aceasta.
              a                                    s

   Dup˘ cum vom vedea, la executia unui program Java vor fi create implicit
        a                            ¸
dou˘ obiecte de tip ClassLoader pentru ˆ arcarea ˆ memorei a claselor
    a                                         ınc˘        ın
proprii ale aplicatiei. Exist˘ ˆ a posibilitarea de a crea noi tipuri derivate din
                  ¸          a ıns˘
ClassLoader specializate pentru ˆ arcarea claselor conform unor specificatii
                                    ınc˘                                        ¸
anume care s˘ realizeze diverse optimiz˘ri. Astfel, ˆ arcarea unei clase poate
              a                          a           ınc˘
determina ˆ arcarea unor altor clase care sigur vor fi folosite ˆ
            ınc˘                                                    ımpreun˘ cu
                                                                             a
prima, sau a unor resurse ce sunt necesare function˘rii acesteia, etc.
                                                   ¸ a


    Incepˆnd cu versiunea 1.2 de Java, a fost introdus un model de tip delegat,
         a
ˆ care class loader-ele sunt dispuse ierarhic ˆ
ın                                            ıntr-un arbore, r˘d˘cina acestuia
                                                                a a
fiind class loader-ul primordial. Fiecare instanta de tip ClassLoader va avea
                                                 ¸
a¸adar un p˘rinte (evident, mai putin r˘d˘cina), acesta fiind specificat la
 s           a                         ¸   a a
crearea sa. In momentul cˆnd este solicitat˘ ˆ arcarea unei clase, un class-
                           a                  a ınc˘
loader poate delega ˆ primul rˆnd operatiunea de ˆ arcare p˘rintelui s˘u
                      ın         a          ¸          ınc˘         a       a
care va delega la rˆndul s˘u cererea mai departe pˆn˘ la class loader-ul
                     a       a                            a a
primordial sau pˆn˘ unul din ace¸tia reu¸e¸te s˘ o ˆ
                 a a               s       s s     a ıncarce. Abia ˆ cazul ˆ
                                                                      ın     ın
care nici unul din ace¸tia nu a reu¸it, va ˆ
                        s            s      ıncerca s˘ execute operatiunea de
                                                     a                  ¸
ˆ arcare a clasei. Dac˘ nici ea nu va reu¸i, va fi aruncat˘ o exceptie de tipul
ınc˘                     a                s                 a         ¸
ClassNotFoundException. De¸i acest comportament nu este obligatoriu,
                                   s
ˆ multe situatii el este de preferat, pentru a minimiza ˆ arcarea aceleia¸i
ın             ¸                                             ınc˘             s
clase de mai multe ori, folosind class loader-e diferite.
16.1. INCARCAREA CLASELOR ˆ MEMORIE
         ˘                IN                                              447

   Implicit, Java 2 JVM ofer˘ trei class loader-e, unul primordial ¸i dou˘
                             a                                     s     a
proprii, cunoscute sub numele de:
   • Boostrap Class Loader - Class loader-ul primordial. Acesta este re-
     sponsabil cu ˆ arcarea claselor din distributia Java standard (cele din
                  ınc˘                           ¸
     pachetele java.*, javax.*, etc.).

   • Extension Class Loader - Utilizat pentru ˆ arcarea claselor din direc-
                                              ınc˘
     toarele extensiilor JRE.

   • System Class Loader - Acesta este responsabil cu ˆ arcarea claselor
                                                      ınc˘
     proprii aplicatiilor Java (cele din CLASSPATH). Tipul acestuia este
                   ¸
     java.lang.URLClassLoader.




    Intrucˆt tipurile de date Java pot fi ˆ arcate folosind diverse instante
           a                              ınc˘                           ¸
de tip ClassLoader, fiecare obiect Class va retine class loader-ul care a
                                                 ¸
fost folosit pentru ˆ arcare, acesta putˆnd fi obtinut cu metoda getClass-
                    ınc˘                a       ¸
Loader.


    Inc˘rcarea dinamic˘ a unei clase ˆ memorie se refer˘ la faptul c˘ nu
        a               a              ın                   a            a
cunoastem tipul acesteia decˆt la executia preogramului, moment ˆ care
        ¸                      a          ¸                           ın
putem solicita ˆ arcarea sa, specificˆnd numele s˘u complet prin intermediul
               ınc˘                  a            a
unui ¸ir de caractere. Acest lucru poate fi realizat prin mai multe modalit˘¸i,
      s                                                                    at
cele mai comune metode fiind:
448                     CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

   • loadClass apelat˘ pentru un obiect de tip ClassLoader
                     a


        ClassLoader loader = new MyClassLoader();
        loader.loadClass("NumeCompletClasa");

   • Class.forName
     Aceast˘ metoda va ˆ arca respectiva clas˘ folosind class loader-ul
            a              ınc˘              a
     obiectului curent (care o apeleaz˘):
                                      a

        Class c = Class.forName("NumeCompletClasa");
        // echivalent cu
        ClassLoader loader = this.getClass().getClassLoader();
        loader.loadClass("ClasaNecunoscuta");

        // Clasele standard pot fi si ele incarcate astfel
        Class t = Class.forName("java.lang.Thread");

    Dac˘ dorim s˘ instantiem un obiect dintr-o clas˘ ˆ arcat˘ dinamic putem
        a        a        ¸                           a ınc˘    a
folosi metoda newInstance, cu conditia s˘ existe constructorul f˘r˘ argu-
                                          ¸   a                    aa
mente pentru clasa respectiv˘. Dup˘ cum vom vedea ˆ sectiunea urm˘toare,
                               a      a                   ın ¸       a
mai exist˘ ¸i alte posibilit˘¸i de a instantia astfel de obiecte.
          as                at              ¸
  Class c = Class.forName("java.awt.Button");
  Button b = (Button) c.newInstance();



    Folosirea interfetelor sau a claselor abstracte ˆ
                     ¸                              ımpreun˘ cu ˆ arcarea di-
                                                             a     ınc˘
namic˘ a claselor ofer˘ un mecanism extrem de puternic de lucru ˆ Java.
      a                 a                                               ın
Vom detalia acest lucru prin intermediul unui exepmplu. S˘ presupunem
                                                                  a
c˘ dorim s˘ cre˘m o aplicatie care s˘ genereze aleator un vector de numere
 a          a     a          ¸         a
dup˘ care s˘ aplice o anumit˘ functie acestui vector. Numele functiei care
    a        a                  a     ¸                                 ¸
trebuie apelat˘ va fi introdus de la tastatur˘, iar implementarea ei va fi
                a                                a
continut˘ ˆ
    ¸    a ıntr-o clas˘ a directorului curent. Toate functiile vor extinde clasa
                      a                                  ¸
abstract˘ Functie. In felul acesta, aplicatia poate fi extins˘ cu noi functii
         a       ¸                           ¸                  a             ¸
f˘r˘ a schimba codul ei, tot ce trebuie s˘ facem fiind s˘ scriem noi clase care
 aa                                       a             a
extind Functie ¸i s˘ implement˘m metoda executa. Aceasta va returna 0
                   s a             a
dac˘ metoda s-a executat corect, −1 ˆ caz contrar.
    a                                   ın
16.1. INCARCAREA CLASELOR ˆ MEMORIE
         ˘                IN                                             449

         Listing 16.1: Exemplu de ˆ arcare dinamic˘ a claselor
                                  ınc˘            a
import java . util .*;
import java . io .*;

public class TestFunctii {

  public static void main ( String args []) throws IOException {
    // Generam un vector aleator
    int n = 10;
    int v [] = new int [ n ];
          Random rand = new Random () ;
    for ( int i =0; i < n ; i ++)
      v [ i ] = rand . nextInt (100) ;

    // Citim numele unei functii
    BufferedReader stdin = new BufferedReader (
        new InputStreamReader ( System . in ) ) ;
    String numeFunctie = " " ;

    while (! numeFunctie . equals ( " gata " ) ) {

       System . out . print ( "  nFunctie : " ) ;
           numeFunctie = stdin . readLine () ;

           try {
             // Incarcam clasa
             Class c = Class . forName ( numeFunctie ) ;

              // Cream un obiect de tip Functie
              Functie f = ( Functie ) c . newInstance () ;

              // Setam vectorul
              f . setVector ( v ) ; // sau f . v = v ;

              // Executam functia
              int ret = f . executa () ;
              System . out . println ( "  nCod returnat : " + ret ) ;

      } catch ( Cl as sNo tF oun d E x c e p t i o n e ) {
        System . err . println ( " Functie inexistenta ! " ) ;
      } catch ( In st ant ia tio n E x c e p t i o n e ) {
        System . err . println ( " Functia nu poate fi instantiata !
           ");
      } catch ( Il le gal Ac ces s E x c e p t i o n e ) {
        System . err . println ( " Functia nu poate fi accesata ! " ) ;
450                           CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

            }
        }
    }
}



                    Listing 16.2: Clasa abstract˘ ce descrie functia
                                                a                ¸
public abstract class Functie {
  public int v [] = null ;
  public void setVector ( int [] v ) {
    this . v = v ;
  }
  public abstract int executa () ;
}



                          Listing 16.3: Un exemplu de functie
                                                          ¸
import java . util .*;
public class Sort extends Functie {
  public int executa () {
        if ( v == null )
          return -1;
        Arrays . sort ( v ) ;
        for ( int i =0; i < v . length ; i ++)
          System . out . print ( v [ i ] + " " ) ;
        return 0;
  }
}



                          Listing 16.4: Alt exemplu de functie
                                                           ¸
public class Max extends Functie {
  public int executa () {
        if ( v == null )
          return -1;

                int max = v [0];
                for ( int i =1; i < v . length ; i ++)
                  if ( max < v [ i ])
                      max = v [ i ];
                System . out . print ( max ) ;
                return 0;
16.1. INCARCAREA CLASELOR ˆ MEMORIE
         ˘                IN                                               451

    }
}




    Un obiect de tip URLClassLoader mentine o list˘ de adrese URL de unde
                                           ¸        a
va ˆıncerca s˘ ˆ
             a ıncarce ˆ memorie clasa al c˘rei nume ˆ specific˘m ca argu-
                        ın                   a          ıl      a
ment al metodelor de mai sus. Implicit, la crearea class loader-ului aceast˘
                                                                           a
list˘ este completat˘ cu informatiile din variabila sistem CLASSPATH sau cu
    a                a           ¸
cele specificate prin optiunea -classpath la lansarea aplicatiei. Folosind
                           ¸                                  ¸
metoda getURLs putem afla aceste adrese, iar cu addURL putem ad˘uga     a
o nou˘ adres˘ de c˘utare a claselor. Bineˆ ¸eles, adresele URL pot specifica
       a      a     a                      ınt
¸i directoare ale sistemului de fi¸iere local. S˘ presupunem c˘ ˆ directorul
s                                s             a              a ın
c:clasedemo exist˘ clasa cu numele Test, aflat˘ ˆ pachetul demo ¸i dorim
                       a                          a ın              s
s˘ o ˆ arc˘m dinamic ˆ memorie:
  a ınc˘ a                ın

    // Obtinem class loaderul curent
    URLClassLoader urlLoader =
      (URLClassLoader) this.getClass().getClassLoader();
    // Adaugam directorul sub forma unui URL
    urlLoader.addURL(new File("c:clase").toURL());
    // Incarcam clasa
    urlLoader.loadClass("demo.Test");

    Dup˘ ce o clas˘ a fost ˆ arcat˘ folosind un class loader, ea nu va mai
         a            a      ınc˘     a
putea fi desc˘rcat˘ explicit din memorie. In cazul ˆ care dorim s˘ avem posi-
              a     a                               ın             a
bilitatea de a o reˆ arca, deoarece a fost modificat˘ ¸i recompilat˘, trebuie s˘
                   ınc˘                              as             a         a
folosim class-loadere proprii ¸i s˘ instantiem noi obiecte de tip ClassLoader,
                              s a         ¸
ori de cˆte ori dorim s˘ fort˘m reˆ arcarea claselor. Crearea unui class
         a               a     ¸a      ınc˘
loader propriu se face uzual prin extinderea clasei URLClassLoader, o vari-
ant˘ simplist˘ fiind prezentat˘ mai jos:
    a          a                a

public class MyClassLoader extends URLClassLoader{
  public MyClassLoader(URL[] urls){
    super(urls);
  }
}

    Inc˘rcarea claselor folosind clasa nou creat˘ se va face astfel:
       a                                        a
452                    CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

  // La initializare
  URLClassLoader systemLoader =
    (URLClassLoader) this.getClass().getClassLoader();
  URL[] urls = systemLoader.getURLs();

  // Cream class loaderul propriu
  MyClassLoader myLoader = new MyClassLoader(urls);
  myLoader.loadClass("Clasa");
  ...
  // Dorim sa reincarcam clasa
  myLoader.loadClass("Clasa"); // nu functioneaza !

  // Cream alt class loader
  MyClassLoader myLoader = new MyClassLoader(urls);
  myLoader.loadClass("Clasa"); // reincarca clasa


16.2      Mecanismul reflect˘rii
                           a
Mecanismul prin care o clas˘, interfat˘ sau obiect ”reflect˘” la momentul
                              a       ¸a                     a
executiei structura lor intern˘ se nume¸te reflectare (eng. reflection), acesta
      ¸                       a        s
punˆnd la dispozitie metode pentru:
    a             ¸

   • Determinarea clasei unui obiect.

   • Aflarea unor informatii despre o clas˘ (modificatori, superclasa, con-
                         ¸               a
     structori, metode).

   • Instantierea unor clase al c˘ror nume este ¸tiut abia la executie.
           ¸                     a              s                  ¸

   • Setarea sau aflarea atributelor unui obiect, chiar dac˘ numele acestora
                                                          a
     este ¸tiut abia la executie.
          s                  ¸

   • Invocarea metodelor unui obiect al c˘ror nume este ¸tiut abia la executie.
                                         a              s                  ¸

   • Crearea unor vectori a c˘ror dimensiune ¸i tip nu este ¸tiut decˆt la
                             a               s              s        a
     executie.
          ¸

   Suportul pentru reflectare este inclus ˆ distributia standard Java, fiind
                                         ın        ¸
cunoscut sub numele de Reflection API ¸i contine urm˘toarele clase:
                                       s    ¸         a
˘
16.2. MECANISMUL REFLECTARII                                              453

   • java.lang.Class

   • java.lang.Object

   • Clasele din pachetul java.lang.reflect ¸i anume:
                                           s

         – Array
         – Constructor
         – Field
         – Method
         – Modifier


16.2.1     Examinarea claselor ¸i interfetelor
                               s         ¸
Examinarea claselor ¸i interfetelor se realizeaz˘ cu metode ale clasei java.lang.Class,
                     s        ¸                 a
un obiect de acest tip putˆnd s˘ reprezinte atˆt o clas˘ cˆt ¸i o interfat˘,
                            a     a                a       a a s             ¸a
diferentierea acestora f˘cˆndu-se prin intermediul metodei isInterface.
       ¸                a a
    Reflection API pune la dispozitie metode pentru obtinerea urm˘toarelor
                                    ¸                     ¸             a
informatii:
        ¸


   Aflarea instantei Class corespunz˘tor unui anumit obiect sau tip de
                ¸                  a
date:

  Class c = obiect.getClass();
  Class c = java.awt.Button.class;
  Class c = Class.forName("NumeClasa");

Tipurile primitive sunt descrise ¸i ele de instante de tip Class avˆnd forma
                                 s               ¸                 a
TipPrimitiv.class: int.class, double.class, etc., diferentierea lor f˘cˆndu-
                                                               ¸        a a
se cu ajutorul metodei isPrimitive.


   Aflarea numelui unei clase - Se realizeaz˘ cu metoda getName:
                                           a

  Class clasa = obiect.getClass();
  String nume = clasa.getName();
454                    CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

    Aflarea modificatorilor unei clase - Se realizeaz˘ cu metoda getModifiers,
                                                       a
aceasta returnˆnd un num˘r ˆ
                a          a ıntreg ce codific˘ toti modificatorii clasei. Pen-
                                             a ¸
tru a determina u¸or prezenta unui anumit modificator se folosesc metodele
                    s        ¸
statice ale clasei Modifier isPublic, isAbstract ¸i isFinal:
                                                     s
  Class clasa = obiect.getClass();
  int m = clasa.getModifiers();
  String modif = "";
  if (Modifier.isPublic(m))
    modif += "public ";
  if (Modifier.isAbstract(m))
    modif += "abstract ";
  if (Modifier.isFinal(m))
    modif += "final ";
  System.out.println(modif + "class" + c.getName());



    Aflarea superclasei - Se realizeaz˘ cu metoda getSuperclass ce re-
                                       a
turneaz˘ o instant˘ de tip Class, corespunz˘toare tipului de date al super-
        a        ¸a                        a
clasei sau null pentru clasa Object.
  Class c = java.awt.Frame.class;
  Class s = c.getSuperclass();
  System.out.println(s); // java.awt.Window

  Class c = java.awt.Object.class;
  Class s = c.getSuperclass(); // null



   Aflarea interfetelor implementate de o clas˘ sau extinse de o interfat˘
                   ¸                         a                         ¸a
- Se realizeaz˘ cu metoda getInterfaces, ce returneaz˘ un vector de tip
              a                                      a
Class[].
  public void interfete(Class c) {
    Class[] interf = c.getInterfaces();
    for (int i = 0; i < interf.length; i++) {
      String nume = interf[i].getName();
      System.out.print(nume + " ");
˘
16.2. MECANISMUL REFLECTARII                                               455

     }
  }
  ...
  interfete(java.util.HashSet.class);
  // Va afisa interfetele implementate de HashSet:
  // Cloneable, Collection, Serializable, Set


  interfete(java.util.Set);
  // Va afisa interfetele extinse de Set:
  // Collection



    Aflarea variabilelor membre - Se realizeaz˘ cu una din metodele
                                                       a
getFields sau getDeclaredFields, ce returnez˘ un vector de tip Field[],
                                                   a
diferenta ˆ
       ¸ ıntre cele dou˘ constˆnd ˆ faptul c˘ prima returneaz˘ toate vari-
                        a       a ın            a                 a
abilele membre, inclusiv cele mo¸tenite, ˆ timp ce a doua le returnez˘ doar
                                  s      ın                             a
pe cele declarate ˆ cadrul clasei. La rˆndul ei, clasa Field pune la dispozitie
                  ın                   a                                    ¸
metodele getName, getType ¸i getModifiers pentru a obtine numele, tipul,
                              s                             ¸
respectiv modificatorii unei variabile membru.
    Cu ajutorul metodei getField este posibil˘ obtinerea unei referinte la o
                                                 a ¸                    ¸
variabil˘ mebr˘ cu un anumit nume specificat.
        a      a


    Aflarea constructorilor - Se realizeaz˘ cu metodele getConstructors
                                          a
sau getDeclaredConstructors, ce returneaz˘ un vector de tip Constructor[].
                                            a
Clasa Constructor pune la dispozitie metodele getName, getParameterTypes,
                                  ¸
getModifiers, getExceptionTypes pentru a obtine toate informatiile legate
                                               ¸                 ¸
de respectivul constructor.
    Cu ajutorul metodei getConstructor este posibil˘ obtinerea unei referinte
                                                   a ¸                     ¸
la constructor cu o signatur˘ specificat˘.
                            a          a


   Aflarea metodelor - Se realizeaz˘ cu metodele getMethods sau
                                    a
getDeclaredMethods, ce returneaz˘ un vector de tip Method[]. Clasa Method
                                 a
pune la dispozitie metodele getName, getParameterTypes, getModifiers,
               ¸
getExceptionTypes, getReturnType pentru a obtine toate informatiile legate
                                               ¸                ¸
456                    CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

de respectiva metod˘.
                   a
   Cu ajutorul metodei getMethod este posibil˘ obtinerea unei referinte la
                                             a ¸                     ¸
o metod˘ cu o signatur˘ specificat˘.
        a             a          a


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


   Aflarea clasei de acoperire - Se realizeaz˘ cu metoda getDeclaringClass.
                                              a
Aceast˘ metod˘ o reg˘sim ¸i ˆ clasele Field, Constructor, Method, pentru
       a       a     a     s ın
acestea returnˆnd clasa c˘rei ˆ apartine variabila, constructorul sau metoda
              a          a ıi       ¸
respectiv˘.
         a

16.2.2     Manipularea obiectelor
Pe lˆng˘ posibilitatea de a examina structura unei anumite clase la momentul
    a a
executiei, folosind Reflection API avem posibilitatea de a lucra dinamic cu
     ¸
obiecte, bazˆndu-ne pe informatii pe care le obtinem abia la executie.
             a                  ¸               ¸                   ¸


    Crearea obiectelor
Dup˘ cum stim, crearea obiectelor se realizeaz˘ cu operatorul new urmat de
     a       ¸                                 a
un apel la un constructor al clasei pe care o instantiem. In cazul ˆ care
                                                     ¸                 ın
numele clasei nu este cunoscut decˆt la momentul executiei nu mai putem
                                    a                     ¸
folosi aceast˘ metod˘ de instantiere. In schimb, avem la dispozitie alte dou˘
             a      a          ¸                                ¸           a
variante:

   • Metoda newInstance din clasa java.lang.Class
     Aceasta permite instantierea unui obiect folosind constructorul f˘r˘ ar-
                             ¸                                        aa
     gumente al acestuia. Dac˘ nu exist˘ un astfel de constructor sau nu este
                               a         a
     accesibil vor fi generate exceptii de tipul InstantiationException, re-
                                   ¸
     spectiv IllegalAccessException.

         Class c = Class.forName("NumeClasa");
         Object o = c.newInstance();

         // Daca stim tipul obiectului
˘
16.2. MECANISMUL REFLECTARII                                            457

       Class c = java.awt.Point.class;
       Point p = (Point) c.newInstance();

   • Metoda newInstance din clasa Constructor
     Aceasta permite instantierea unui obiect folosind un anumit construc-
                             ¸
     tor, cel pentru care se face apelul. Evident, aceast˘ solutie presupune
                                                         a     ¸
     ˆ primul rˆnd obtinerea unui obiect de tip Constructor cu o anumit˘
     ın         a      ¸                                                   a
     signatur˘, apoi specificarea argumentelor la apelarea sa. S˘ rescriem
              a                                                    a
     exemplul de mai sus, apelˆnd constructorul cu dou˘ argumente al clasei
                               a                        a
     Point.

       Class clasa = java.awt.Point.class;

       // Obtinem constructorul dorit
       Class[] signatura = new Class[] {int.class, int.class};
       Constructor ctor = clasa.getConstructor(signatura);

       // Pregatim argumentele
       // Ele trebuie sa fie de tipul referinta corespunzator
       Integer x = new Integer(10);
       Integer y = new Integer(20);
       Object[] arg = new Object[] {x, y};

       // Instantiem
       Point p = (Point) ctor.newInstance(arg);

   Exceptii generate de metoda newInstance sunt: InstantiationException,
          ¸
IllegalAccessException, IllegalArgumentException ¸i   s
InvocationTargetException. Metoda getConstructor poate provoca exceptii
                                                                      ¸
de tipul NoSuchMethodException.


    Invocarea metodelor
Invocarea unei metode al c˘rei nume ˆ cunoa¸tem abia la momentul executiei
                          a         ıl     s                          ¸
se realizeaz˘ cu metoda invoke a clasei Method. Ca ¸i ˆ cazul construc-
            a                                          s ın
torilor, trebuie s˘ obtinem ˆ ai o referint˘ la metoda cu signatura core-
                  a ¸       ıntˆ          ¸a
spunz˘toare ¸i apoi s˘ specific˘m argumentele. In plus, mai putem obtine
      a       s       a        a                                     ¸
valoarea returnat˘. S˘ presupunem c˘ dorim s˘ apel˘m metoda contains
                  a a                  a       a    a
458                     CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

a clasei Rectangle care determin˘ dac˘ un anumit punct se g˘se¸te ˆ inte-
                                 a   a                     a s ın
riorul drepunghiului. Metoda contains are mai multe variante, noi o vom
apela pe cea care accept˘ un argument de tip Point.
                        a

  Class clasa = java.awt.Rectangle.class;
  Rectangle obiect = new Rectangle(0, 0, 100, 100);

  // Obtinem metoda dorita
  Class[] signatura = new Class[] {Point.class};
  Method metoda = clasa.getMethod("contains", signatura);

  // Pregatim argumentele
  Point p = new Point(10, 20);
  Object[] arg = new Object[] {p};

  // Apelam metoda
  metoda.invoke(obiect, arg);

    Dac˘ num˘rul argumentelor metodei este 0, atunci putem folosi val-
       a        a
oarea null ˆ locul vectorilor ce reprezint˘ signatura, respectiv parametri
             ın                           a
de apelare ai metodei.
    Exceptiile generate de metoda invoke sunt: IllegalAccessException
          ¸
¸i InvocationTargetException. Metoda getMethod poate provoca exceptii
s                                                                      ¸
de tipul NoSuchMethodException.


   Setarea ¸i aflarea variabilelor membre
             s
Pentur setarea ¸i aflarea valorilor variabilelor membre sunt folosite metodele
                s
set ¸i get ale clasei Field. S˘ presupunem c˘ dorim s˘ set˘m variabila x a
    s                          a                a       a     a
unui obiect de tip Point ¸i s˘ obtinem valoarea variabilei y a aceluia¸i obiect:
                          s a ¸                                       s

  Class clasa = java.awt.Point.class;
  Point obiect = new Point(0, 20);

  // Obtinem variabilele membre
  Field x, y;
  x = clasa.getField("x");
  y = clasa.getField("y");
˘
16.2. MECANISMUL REFLECTARII                                           459

  // Setam valoarea lui x
  x.set(obiect, new Integer(10));

  // Obtinem valoarea lui y
  Integer val = y.get(obiect);

    Exceptiile generate de metodele get, set sunt: IllegalAccessException
         ¸
¸i IllegalArgumentException. Metoda getField poate provoca exceptii de
s                                                                   ¸
tipul NoSuchFieldException.


    Revenind la exemplul din sectiunea anterioar˘ cu apelarea dinamic˘ a
                                    ¸              a                     a
unor functii pentru un vector, s˘ presupunem c˘ exist˘ deja un num˘r ˆ
          ¸                     a               a    a            a ınsemnat
de clase care descriu diferite functii dar acestea nu extind clasa abstract˘
                                     ¸                                     a
Functie. Din acest motiv, solutia anterioar˘ nu mai este viabil˘ ¸i trebuie
                                  ¸           a                  as
s˘ folosim apelarea metodei executa ˆ
 a                                     ıntr-un mod dinamic.

            Listing 16.5: Lucru dinamic cu metode ¸i variabile
                                                  s
import java . lang . reflect .*;
import java . util .*;
import java . io .*;

public class TestFunctii2 {

  public static void main ( String args []) throws IOException {
    // Generam un vector aleator
    int n = 10;
    int v [] = new int [ n ];
          Random rand = new Random () ;
    for ( int i =0; i < n ; i ++)
      v [ i ] = rand . nextInt (100) ;

    // Citim numele unei functii
    BufferedReader stdin = new BufferedReader (
        new InputStreamReader ( System . in ) ) ;
    String numeFunctie = " " ;

    while (! numeFunctie . equals ( " gata " ) ) {

       System . out . print ( "  nFunctie : " ) ;
           numeFunctie = stdin . readLine () ;
460                        CAPITOLUL 16. LUCRUL DINAMIC CU CLASE

                try {
                  // Incarcam clasa
                  Class c = Class . forName ( numeFunctie ) ;

                   // Cream un obiect de tip Functie
                   Object f = c . newInstance () ;

                   // Setam vectorul ( setam direct variabila v )
                   Field vector = c . getField ( " v " ) ;
                   vector . set (f , v ) ;

                   // Apelam metoda ’ executa ’
                   // Folosim null pentru ca nu avem argumente
                   Method m = c . getMethod ( " executa " , null ) ;
                   Integer ret = ( Integer ) m . invoke (f , null ) ;
                   System . out . println ( "  nCod returnat : " + ret ) ;

            } catch ( Exception e ) {
              System . err . println ( " Eroare la apelarea functiei ! " ) ;
            }
        }
    }
}



16.2.3         Lucrul dinamic cu vectori
Vectorii sunt reprezentati ca tip de date tot prin intermediul clasei java.lang.Class,
                        ¸
diferentierea f˘cˆndu-se prin intermediul metodei isArray.
       ¸       a a
    Tipul de date al elementelor din care este format vectorul va fi obtinut cu
                                                                         ¸
ajutorul metodei getComponentType, ce ˆ     ıntoarce o referint˘ de tip Class.
                                                              ¸a

    Point []vector = new Point[10];
    Class c = vector.getClass();
    System.out.println(c.getComponentType());
    // Va afisa: class java.awt.Point

  Lucrul dinamic cu obiecte ce reprezint˘ vectori se realizeaz˘ prin inter-
                                         a                    a
mediul clasei Array. Aceasta contine o serie de metode statice ce permit:
                                ¸

    • Crearea de noi vectori: newInstance

    • Aflarea num˘rului de elemente: getLength
                a
˘
16.2. MECANISMUL REFLECTARII                                           461

   • Setarea / aflarea elementelor: set, get

    Exemplul de mai jos creeaz˘ un vector ce contine numerele ˆ
                              a                 ¸             ıntregi de la
0 la 9:

  Object a = Array.newInstance(int.class, 10);
  for (int i=0; i < Array.getLength(a); i++)
    Array.set(a, i, new Integer(i));

  for (int i=0; i < Array.getLength(a); i++)
    System.out.print(Array.get(a, i) + " ");

Cristian frasinaru curs-practic_de_java

  • 1.
    Curs practic deJava Cristian Fr˘sinaru a
  • 2.
    Cuprins 1 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 . . . . . . . . . . . . . . . . . . . 34 2 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 . . . . . . . . . . . . . . . . . . . . . 75 3 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 . . . . . . . . ¸ . . . . . . . . . . . . 92 4 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 5 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 6 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 . . . . . . . . ¸ . . . . . 159 7 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 . . . . . . . . . . . . . . . . . . 172 8 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 . . . . . . . . . . . . . . . . . . . . . . . . 196 9 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 . . . . . . . . . . . . . . . . . . . . . . 265 10 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 . . . . . . . . . . . 293 11 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 CUPRINS 12 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 . . . . . . . . . . . 378 13 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 ¸ ¸ . . . . . . . . . . 397 14 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 9 15 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 . . . . ¸ . . . . . . . . . . 443 16 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 1 Introducere ˆJava ın 1.1 Ce este Java ? Java este o tehnologie inovatoare lansat˘ de compania Sun Microsystems ˆ a ın 1995, care a avut un impact remarcabil asupra ˆ ıntregii comunit˘¸i a dez- at voltatorilor de software, impunˆndu-se prin calit˘¸i deosebite cum ar fi sim- a at plitate, 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 pe baza c˘ruia sunt construite o serie de platforme destinate implement˘rii de a a aplicatii pentru toate segmentele industriei software. ¸ 1.1.1 Limbajul de programare Java Inainte de a prezenta ˆ detaliu aspectele tehnice ale limbajului Java, s˘ am- ın a intim caracteristicile sale principale, care l-au transformat ˆ ıntr-un interval de timp 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 s 1.1.2 Platforme de lucru Java Limbajul de programare Java a fost folosit la dezvoltarea unor tehnologii ded- icate rezolv˘rii unor probleme din cele mai diverse domenii. Aceste tehnologii a au fost grupate ˆ a¸a numitele platforme de lucru, ce reprezint˘ seturi de ın s a libr˘rii scrise ˆ limbajul Java, precum ¸i diverse programe utilitare, folosite a ın s pentru dezvoltarea de aplicatii sau componente destinate unei anume cate- ¸ gorii de utilizatori.
  • 14.
    1.1. CE ESTEJAVA ? 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 a Internet de la adresa ”http://java.sun.com”. In continuare, vom folosi termenul J2SDK pentru a ne referi la distributia ¸ standard J2SE 1.5 SDK (Tiger). 1.1.3 Java: un limbaj compilat ¸i interpretat s In functie de modul de executie a aplicatiilor, limbajele de programare se ¸ ¸ ¸ ˆ ımpart ˆ dou˘ categorii: ın a • Interpretate: instructiunile sunt citite linie cu linie de un program ¸ numit interpretor ¸i traduse ˆ instructiuni 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 s de 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 a tat de o succesiune de instructiuni specifice unui anumit procesor ¸i unei an- ¸ s umite platforme de lucru reprezentate ˆ format binar astfel ˆ at s˘ poat˘ ın ıncˆ a a fi 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 s de lucru. In timp ce codul ma¸in˘ este executat direct de c˘tre procesor ¸i s a a s poate 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 a care 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 a anumit calculator, pe acesta trebuie s˘ fie instalat˘ o ma¸in˘ virtual˘ Java. a a s a a Acest lucru este realizat automat de c˘tre distributia J2SDK. a ¸ 1.2 Primul program Crearea oric˘rei aplicatii Java presupune efectuarea urm˘torilor pa¸i: a ¸ a s 1. Scriererea codului surs˘ a class 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 ın s˘ se gaseasc˘ metoda main. Clasele aplicatiei se pot gasi fie ˆ a a ¸ ıntr-un singur fi¸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 a sie nefiind acceptat˘. Este recomandat ca fi¸ierul care contine codul surs˘ a s ¸ a al clasei primare s˘ aib˘ acela¸i nume cu cel al clasei, de¸i acest lucru nu a a s s este obligatoriu. S˘ presupunem c˘ am salvat exemplul de mai sus ˆ fi¸ierul a a ın s C: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 ¸ a aplicatiei sau pentru orice fi¸ier/fi¸iere cu extensia java. Compilatorul creeaz˘ ¸ s s a cˆ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.java In 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 a intrare 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 s astfel: java FirstApp Rularea unei aplicatii care nu folose¸te interfat˘ grafic˘, se va face ˆ ¸ s ¸a a ıntr-o fereastr˘ sistem. a
  • 17.
    16 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN Atentie ¸ Un apel de genul java c:introFirstApp.class este gre¸it! s 1.3 Structura lexical˘ a limbajului Java a 1.3.1 Setul de caractere Limbajului Java lucreaz˘ ˆ mod nativ folosind setul de caractere Unicode. a ın Acesta este un standard international care ˆ ¸ ınlocuie¸te vechiul set de caractere s ASCII ¸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 a era posibil˘ reprezentarea a doar 256 de caractere. Primele 256 caractere a Unicode corespund celor ASCII, referirea la celelalte f˘cˆndu-se prin uxxxx, a a unde xxxx reprezint˘ codul caracterului. a O alt˘ caracteristic˘ a setului de caractere Unicode este faptul c˘ ˆ a a a ıntreg intervalul de reprezentare a simbolurilor este divizat ˆ subintervale numite ın blocuri, cˆteva exemple de blocuri fiind: Basic Latin, Greek, Arabic, Gothic, a Currency, 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 cheie Cuvintele rezervate ˆ Java sunt, cu cˆteva exceptii, cele din C++ ¸i au fost ın a ¸ s enumerate ˆ tabelul de mai jos. Acestea nu pot fi folosite ca nume de clase, ın
  • 18.
    ˘ 1.3. STRUCTURA LEXICALAA LIMBAJULUI JAVA 17 interfete, 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 a 1.3.3 Identificatori Sunt secvente nelimitate de litere ¸i cifre Unicode, ˆ ¸ s ıncepˆnd cu o liter˘. Dup˘ a a a cum am mai spus, identificatorii nu au voie s˘ fie identici cu cuvintele rezer- a vate. 1.3.4 Literali Literalii 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 LEXICALAA 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 ın 1.3.5 Separatori Un 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 a 1.3.6 Operatori Operatorii Java sunt, cu mici deosebiri, cei din C++: • atribuirea: = • operatori matematici: +, -, *, /, %, ++, -- . Este permis˘ notatia prescurtat˘ de forma lval op= rval: x += 2 n a ¸ a -= 3 Exist˘ operatori pentru autoincrementare ¸i autodecrementare (post ¸i a s s pre): x++, ++x, n--, --n Evaluarea expresiilor logice se face prin metoda scurtcircuitului: 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 conversion 1.3.7 Comentarii In Java exist˘ trei feluri de comentarii: a • Comentarii pe mai multe linii, ˆ ınchise ˆ ıntre /* ¸i */. s • Comentarii pe mai multe linii care ¸in de documentatie, ˆ t ¸ ınchise ˆ ıntre /** ¸i */. Textul dintre cele dou˘ secvente este automat mutat ˆ s a ¸ ın documentatia aplicatiei de 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 DEDATE SI VARIABILE ¸ 21 1.4 Tipuri de date ¸i variabile s 1.4.1 Tipuri de date In Java tipurile de date se impart ˆ dou˘ categorii: tipuri primitive ¸i ın a s tipuri referint˘. Java porne¸te de la premiza c˘ ”orice este un obiect”, ¸a s a prin urmare tipurile de date ar trebui s˘ fie de fapt definite de clase ¸i toate a s variabilele 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˘ ¸ a exist˘ ¸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) s In alte limbaje de programare formatul ¸i dimensiunea tipurilor primitive de s date pot depinde de platforma pe care ruleaz˘ programul. In Java acest lucru a nu mai este valabil, orice dependent˘ de o anumit˘ platform˘ specific˘ fiind ¸a a a a eliminat˘. a Vectorii, clasele ¸i interfetele sunt tipuri referint˘. Valoarea unei variabile s ¸ ¸a de acest tip este, spre deosebire de tipurile primitive, o referint˘ (adres˘ de ¸a a memorie) c˘tre valoarea sau multimea de valori reprezentat˘ de variabila a ¸ a respectiv˘. a Exist˘ trei tipuri de date din limbajul C care nu sunt suportate de lim- a bajul Java. Acestea sunt: pointer, struct ¸i union. Pointerii au fost s eliminati din cauz˘ c˘ erau o surs˘ constant˘ de erori, locul lor fiind luat de ¸ a a a a tipul referint˘, iar struct ¸i union nu ˆsi mai au rostul atˆt timp cˆt tipurile ¸a s ı¸ a a compuse de date sunt formate ˆ Java prin intermediul claselor. ın
  • 23.
    22 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN 1.4.2 Variabile Variabilele pot fi de tip primitiv sau referinte la obiecte (tip referint˘). In- ¸ ¸a diferent de tipul lor, pentru a putea fi folosite variabilele trebuie declarate ¸i, s eventual, initializate. ¸ • Declararea variabilelor: Tip numeVariabila; • Initializarea variabilelor: Tip numeVariabila = valoare; ¸ • Declararea constantelor: final Tip numeVariabila; Evident, exist˘ posibilitatea de a declara ¸i initializa mai multe variabile a s ¸ sau constante de 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 a criterii: • 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 a categorii: 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 DEDATE 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 a din 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-else if (expresie-logica) { ... } if (expresie-logica) { ... } else { ... } switch-case switch (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 ¸ a de tipuri primitive. 1.5.2 Instructiuni de salt ¸ for for(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 while while (expresie-logica) { ... } do-while do { ... } while (expresie-logica);
  • 27.
    26 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN 1.5.3 Instructiuni pentru tratarea exceptiilor ¸ ¸ Instructiunile pentru tratarea exceptiilor sunt try-catch-finally, respectiv ¸ ¸ throw ¸i vor 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 ın de genul: break numeEticheata sau continue numeEticheta, utile pentru a controla punctul de ie¸ire dintr-o structur˘ repetitiv˘, ca ˆ s a a ınexemplul de mai jos: i=0; eticheta: while (i < 10) { System.out.println("i="+i); j=0; while (j < 10) { j++; if (j==5) continue eticheta; if (j==7) break eticheta; System.out.println("j="+j); } i++; } 1.6 Vectori 1.6.1 Crearea unui vector Crearea 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 genul mente vor fi cuprinse ˆ s ¸ Tip numeVector[nrElemente], alocarea memoriei f˘cˆndu-se doar prin in- a a termediul opearatorului new. int v[10]; //ilegal int v[] = new int[10]; //corect 1.6.2 Tablouri multidimensionale In Java tablourile multidimensionale sunt de fapt vectori de vectori. De exemplu, 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 a elemente iar matrice[i][j] este elementul de pe linia i ¸i coloana j. s 1.6.3 Dimensiunea unui vector Cu ajutorul variabilei length se poate afla num˘rul de elemente al unui a vector. int []a = new int[5]; // a.length are valoarea 5 int m[][] = new int[5][10]; // m[0].length are valoarea 10 Pentru a ˆ ¸elege modalitatea de folosire a lui length trebuie mentionat c˘ ınt ¸ a fiecare vector este de fapt o instant˘ a unei clase iar length este o variabil˘ ¸a a public˘ a acelei clase, ˆ care este retinut num˘rul maxim de elemente al a ın ¸ a vectorului.
  • 30.
    1.6. VECTORI 29 1.6.4 Copierea vectorilor Copierea elementelor unui vector a ˆıntr-un alt vector b se poate face, fie element cu element, fie cu ajutorul metodei System.arraycopy, ca ˆ exem- ın plele de mai jos. Dup˘ cum vom vedea, o atribuire de genul b = a are alt˘ a a semnificatie decˆt copierea elementelor lui a ˆ b ¸i nu poate fi folosit˘ ˆ ¸ a ın s a ın acest scop. int a[] = {1, 2, 3, 4}; int b[] = new int[4]; // Varianta 1 for(int i=0; i<a.length; i++) b[i] = a[i]; // Varianta 2 System.arraycopy(a, 0, b, 0, a.length); // Nu are efectul dorit b = a; 1.6.5 Sortarea vectorilor - clasa Arrays In Java s-a pus un accent deosebit pe implementarea unor structuri de date ¸i algoritmi care s˘ simplifice proceseul de crearea a unui algoritm, program- s a atorul trebuind s˘ se concentreze pe aspectele specifice problemei abordate. a Clasa java.util.Arrays ofer˘ diverse metode foarte utile ˆ lucrul cu vec- a ın tori 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 a 1.6.6 Vectori cu dimensiune variabil˘ ¸i eterogeni as Implement˘ri ale vectorilor cu num˘r variabil de elemente sunt oferite de a a clase 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 s din elemente de tip char, un obiect de tip String sau un obiect de tip StringBuffer. 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 aceste clase este c˘ StringBuffer pune la dispozitie metode pentru modificarea a ¸ continutului ¸irului, cum ar fi: append, insert, delete, reverse. ¸ s Uzual, cea mai folosit˘ modalitate de a lucra cu ¸iruri este prin intermediul a s clasei String, care are ¸i unele particularit˘¸i fat˘ de restul claselor menite s˘ s at ¸a a simplifice cˆt mai mult folosirea ¸irurilor de caractere. Clasa StringBuffer a s va fi utilizat˘ predominant ˆ aplicatii dedicate proces˘rii textelor cum ar fi a ın ¸ a editoarele de texte. Exemple echivalente de declarare a unui ¸ir:s String s = "abc"; String s = new String("abc"); char data[] = {’a’, ’b’, ’c’}; String s = new String(data); Observati prima variant˘ de declarare a ¸irului s din exemplul de mai sus ¸ a s - de altfel, cea mai folosit˘ - care prezint˘ o particularitate a clasei String a a fata de restul claselor Java referitoare la instantierea obiectelor sale. ¸ ¸
  • 32.
    ˘ 1.8. FOLOSIREA ARGUMENTELORDE 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 a permite concatenarea ¸irurilor cu obiecte de orice tip care au o reprezentare s de 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 adunare iar al doilea +, cel de concatenare a ¸irurilor. s 1.8 Folosirea argumentelor de la linia de co- mand˘ a 1.8.1 Transmiterea argumentelor O aplicatie Java poate primi oricˆte argumente de la linia de comanda ˆ ¸ a ın momentul lans˘rii ei. Aceste argumente sunt utile pentru a permite utiliza- a torului s˘ specifice diverse optiuni legate de functionarea aplicatiei sau s˘ a ¸ ¸ ¸ a furnizeze anumite date initiale programului. ¸ Atentie ¸ Programele care folosesc argumente de la linia de comand˘ nu sunt 100% a pure Java, deoarece unele sisteme de operare, cum ar fi Mac OS, nu au ˆ ın mod 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 ¸ a unui 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 ¸ a astfel: java Sortare persoane.txt A¸adar, formatul general pentru lansarea unei aplicatii care prime¸te argu- s ¸ s mente de la linia de comand˘ este: a java NumeAplicatie [arg0 arg1 . . . argn] In cazul ˆ care sunt mai multe, argumentele trebuie separate prin spatii ın ¸ iar dac˘ unul dintre argumente contine spatii, atunci el trebuie pus ˆ a ¸ ¸ ıntre ghilimele. Evident, o aplicatie poate s˘ nu primeasc˘ nici un argument sau ¸ a a poate s˘ ignore argumentele primite de la linia de comand˘. a a 1.8.2 Primirea argumentelor In momentul lans˘rii unei aplicatii interpretorul parcurge linia de comand˘ cu a ¸ a care a fost lansat˘ aplicattia ¸i, ˆ cazul ˆ care exist˘, transmite programului a ¸ s ın ın a argumentele specificate sub forma unui vector de ¸iruri. Acesta este primit s de aplicatie ca parametru al metodei main. Reamintim c˘ formatul metodei ¸ a main din clasa principal˘ este: a public static void main (String args[]) Vectorul args primit ca parametru de metoda main va contine toate argu- ¸ mentele transmise programului de la linia de comand˘. a In cazul apelului java Sortare persoane.txt vectorul args va contine un ¸ singur element pe prima s˘ pozitie: args[0]="persoane.txt". a ¸ Vectoru args este instantiat cu un num˘r de elemente egal cu num˘rul ar- ¸ a a gumentelor primite de la linia de comand˘. A¸adar, pentru a afla num˘rul de a s a argumente primite de program este suficient s˘ afl˘m dimensiunea vectorului a a args prin intermediul atributului length:
  • 34.
    ˘ 1.8. FOLOSIREA ARGUMENTELORDE 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 a de la linia de comand˘ necesare pentru functionarea sa ¸i, ˆ caz contrar, s˘ a ¸ s ın a afi¸eze un mesaj de avertizare sau s˘ foloseasc˘ ni¸te valori implicite, ca ˆ s a a s ın exemplul de mai jos: public class Salut { public static void main (String args[]) { if (args.length == 0) { System.out.println("Numar insuficient de argumente!"); System.exit(-1); //termina aplicatia } String nume = args[0]; //exista sigur String prenume; if (args.length >= 1) prenume = args[1]; else prenume = ""; //valoare implicita System.out.println("Salut " + nume + " " + prenume); } } Spre deosebire de limbajul C, vectorul primit de metoda main nu contine¸ pe prima pozitie numele aplicatiei, ˆ ¸ ¸ ı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 s pe ecran a argumentelor primite de la linia de comand˘: a public class Afisare { public static void main (String[] args) { for (int i = 0; i < args.length; i++) System.out.println(args[i]);
  • 35.
    34 CAPITOLUL 1. INTRODUCERE ˆ JAVA IN } } Un apel de genul java Afisare Hello Java va produce urm˘torul rezul- a tat (aplicatia a primit 2 argumente): ¸ Hello Java Apelul java Afisare "Hello Java" va produce ˆ a alt rezultat (aplicatia ıns˘ ¸ a primit un singur argument): Hello Java 1.8.3 Argumente numerice Argumentele de la linia de comand˘ sunt primite sub forma unui vector de a ¸iruri (obiecte de tip String). In cazul ˆ care unele dintre acestea reprezint˘ s ın a valori numerice ele vor trebui convertite din ¸iruri ˆ numere. Acest lucru s ın se realizeaz˘ cu metode de tipul parseTipNumeric aflate ˆ clasa corespun- a ın zatoare tipului ˆ care vrem s˘ facem conversia: Integer, Float, Double, ın a etc. S˘ consider˘m, de exemplu, c˘ aplicatia Power ridic˘ un numar real la o a a a ¸ a putere ˆıntreag˘, argumentele fiind trimise de la linia de comand˘ sub forma: a a java Power "1.5" "2" //ridica 1.5 la puterea 2 Conversia celor dou˘ argumente ˆ numere se va face astfel: a ın public class Power { public static void main(String args[]) { double numar = Double.parseDouble(args[0]); int putere = Integer.parseInt(args[1]); System.out.println("Rezultat=" + Math.pow(numar, putere)); } } Metodele de tipul parseTipNumeric pot produce exceptii (erori) de tipul ¸ NumberFormatException ˆ cazul ˆ care ¸irul primit ca parametru nu reprezint˘ ın ın s a un numar de tipul respectiv. Tratarea acestor exceptii este prezentat˘ ˆ ¸ a ın capitolul ”Exceptii”. ¸
  • 36.
    Capitolul 2 Obiecte ¸iclase s 2.1 Ciclul de viat˘ al unui obiect ¸a 2.1.1 Crearea obiectelor In Java, ca ˆ orice limbaj de programare orientat-obiect, crearea obiectelor ın se 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 ¸ a obiecte 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 a care este responsabil cu initializarea obiectului cu valorile implicite. Dup˘ ¸ a cum 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 a parametrii respectivi. Fiecare clas˘ are un set de constructori care se ocup˘ cu initializare a a ¸ obiectelor nou create. De exemplu, clasa Rectangle are urm˘torii construc- a tori: 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 s linie (cazul cel mai uzual): Rectangle patrat = new Rectangle(0, 0, 100, 100); Obiecte anonime Este posibil˘ ¸i crearea unor obiecte anonime care servesc doar pentru a s initializarea altor obiecte, caz ˆ care etapa de declarare a referintei obiectului ¸ ın ¸ nu mai este prezent˘: a Rectangle patrat = new Rectangle(new Point(0,0), new Dimension(100, 100));
  • 38.
    ¸˘ 2.1. CICLUL DEVIATA AL UNUI OBIECT 37 Spatiul de memorie nu este pre-alocat ¸ Declararea unui obiect nu implic˘ sub nici o form˘ alocarea de spatiu a a ¸ de memorie pentru acel obiect. Alocarea memoriei se face doar la apelul operatorului new. Rectangle patrat; patrat.x = 10; //Eroare - lipseste instantierea 2.1.2 Folosirea obiectelor Odat˘ un obiect creat, el poate fi folosit ˆ urm˘toarele sensuri: aflarea unor a ın a informatii despre obiect, schimbarea st˘rii sale sau executarea unor actiuni. ¸ a ¸ Aceste lucruri se realizeaza prin aflarea sau schimbarea valorilor variabilelor sale, 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 ın acces pe care le ofer˘ variabilele respective celorlalte clase. (vezi ”Modifica- a tori de acces pentru membrii unei clase”) Apelul unei metode se face prin obiect.metoda([parametri]). Rectangle patrat = new Rectangle(0, 0, 100, 200); patrat.setLocation(10, 20); //schimba originea patrat.setSize(200, 300); //schimba dimensiunea Se observ˘ c˘ valorile variabilelor pot fi modificate indirect prin inter- a a mediul metodelor sale. Programarea orientat˘ obiect descurajeaz˘ folosirea a a direct˘ a variabilelor unui obiect deoarece acesta poate fi adus ˆ st˘ri in- a ın a consistente (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 a orilor variabilelor sale. Acestea se numesc metode de accesare, sau metode setter - getter ¸i au numele de forma setVariabila, respectiv getVariabila. s patrat.width = -100; //stare inconsistenta patrat.setSize(-100, -200); //metoda setter //metoda setSize poate sa testeze daca noile valori sunt //corecte si sa valideze sau nu schimbarea lor 2.1.3 Distrugerea obiectelor Multe limbaje de programare impun ca programatorul s˘ ¸in˘ evidenta obiectelor at a ¸ create ¸i s˘ le distrug˘ ˆ mod explicit atunci cˆnd nu mai este nevoie de ele, s a a ın a cu alte cuvinte s˘ administreze singur memoria ocupat˘ de obiectele sale. a a Practica a demonstrat c˘ aceast˘ tehnic˘ este una din principalele furnizoare a a a de erori ce duc la functionarea defectuoas˘ a programelor. ¸ a In Java programatorul nu mai are responsabilitatea distrugerii obiectelor sale ˆ ıntrucˆt, ˆ momentul rul˘rii unui program, simultan cu interpretorul a ın a Java, ruleaz˘ ¸i un proces care se ocup˘ cu distrugerea obiectelor care nu a s a mai sunt folosite. Acest proces pus la dispozitie de platforma Java de lucru ¸ se nume¸te garbage collector (colector de gunoi), prescurtat gc. s Un obiect este eliminat din memorie de procesul de colectare atunci cˆnd a nu 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- a cut˘ 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 a eliminate 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 ın sunt 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 a obiect posibilitatea ”s˘ curete dup˘ el”, apelˆnd metoda de finalizare a obiec- a ¸ a a tului respectiv. Uzual, ˆ timpul finaliz˘rii un obiect ˆsi inchide fisierele ¸i ın a ı¸ s socket-urile folosite, distruge referintele c˘tre alte obiecte (pentru a u¸sura ¸ a s sarcina colectorului de gunoaie), etc. Codul pentru finalizarea unui obiect trebuie scris ˆ ıntr-o metod˘ special˘ a a numita finalize a clasei ce descrie obiectul respectiv. (vezi ”Clasa Object”) Atentie ¸ Nu confundati metoda finalize din Java cu destructorii din C++. Metoda finalize nu are rolul de a distruge obiectul ci este apelat˘ automat ˆ a ınainte de eliminarea obiectului respectiv din memorie. 2.2 Crearea claselor 2.2.1 Declararea claselor Clasele reprezint˘ o modalitate de a introduce noi tipuri de date ˆ a ıntr-o aplicatie 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 s sunt:
  • 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 a clas˘ este subclas˘ a unei alte clase cu numele NumeSuperclasa sau/¸i c˘ a a s a implementeaz˘ una sau mai multe interfete, ale c˘ror nume trebuie separate a ¸ a prin virgul˘. a 2.2.2 Extinderea claselor Spre deosebire de alte limbaje de programare orientate-obiect, Java permite doar mo¸tenirea simpl˘, ceea ce ˆ s a ıneamn˘ c˘ o clas˘ poate avea un singur a a a p˘rinte (superclas˘). Evident, o clas˘ poate avea oricˆti mo¸tenitori (sub- a a a a s clase), de unde rezult˘ c˘ multimea tuturor claselor definite ˆ Java poate fi a a ¸ ın vazut˘ ca un arbore, r˘d˘cina acestuia fiind clasa Object. A¸adar, Object a a a s este singura clas˘ care nu are p˘rinte, fiind foarte important˘ ˆ modul de a a a ın lucru 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 s care nu sunt private. 2.2.3 Corpul unei clase Corpul unei clase urmeaz˘ imediat dup˘ declararea clasei ¸i este cuprins ˆ a a s ıntre acolade. Continutul acestuia este format din: ¸ • Declararea ¸i, eventual, initializarea variabilelor de instant˘ ¸i de clas˘ s ¸ ¸a s a (cunoscute ˆ ımpreun˘ ca variabile membre). a • Declararea ¸i implementarea constructorilor. s • Declararea ¸i implementarea metodelor de instanta ¸i de clas˘ (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 ın clasei, urmˆnd ca implementare s˘ fie facut˘ ˆ afara ei. Implementarea a a a ın metodelor unei clase trebuie s˘ se fac˘ obligatoriu ˆ corpul clasei. a a ın // C++ class A { void metoda1(); int metoda2() { // Implementare } } A::metoda1() { // Implementare }
  • 43.
    42 CAPITOLUL 2. OBIECTE SI CLASE ¸ // Java class A { void metoda1(){ // Implementare } void metoda2(){ // Implementare } } Variabilele unei clase pot avea acela¸i nume cu metodele clasei, care poate s fi chiar numele clasei, f˘r˘ a exista posibilitatea aparitiei vreunei ambiguit˘¸i aa ¸ at din 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 at dovedind 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 a 2.2.4 Constructorii unei clase Constructorii unei clase sunt metode speciale care au acela¸i nume cu cel s al clasei, nu returneaz˘ nici o valoare ¸i sunt folositi pentru initializarea a s ¸ ¸ obiectelor acelei clase ˆ momentul instantierii lor. ın ¸ class NumeClasa { [modificatori] NumeClasa([argumente]) { // Constructor
  • 44.
    2.2. CREAREA CLASELOR 43 } } O clas˘ poate avea unul sau mai multi constructori care trebuie ˆ a s˘ a ¸ ıns˘ a difere prin lista de argumente primite. In felul acesta sunt permise diverse tipuri de initializ˘ri ale obiectelor la crearea lor, ˆ functie de num˘rul para- ¸ a ın ¸ a metrilor 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 a class Dreptunghi { double x, y, w, h; Dreptunghi(double x1, double y1, double w1, double h1) { // Cel mai general constructor x=x1; y=y1; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi(double w1, double h1) { // Constructor cu doua argumente x=0; y=0; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi() { // Constructor fara argumente x=0; y=0; w=0; h=0; System.out.println("Instantiere dreptunghi"); } } Constructorii sunt apelati automat la instantierea unui obiect. In cazul ¸ ¸ ˆ care dorim s˘ apel˘m explicit constructorul unei clase folosim expresia ın a a this( argumente ), care apeleaz˘ constructorul corespunz˘tor (ca argumente) al clasei respec- a a tive. 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 ¸ s mesajului ”Instantiere dreptunghi”), clasa de mai sus poate fi rescris˘ astfel: a class Dreptunghi { double x, y, w, h; Dreptunghi(double x1, double y1, double w1, double h1) { // Implementam doar constructorul cel mai general x=x1; y=y1; w=w1; h=h1; System.out.println("Instantiere dreptunghi"); } Dreptunghi(double w1, double h1) { this(0, 0, w1, h1); // Apelam constructorul cu 4 argumente } Dreptunghi() { this(0, 0); // Apelam constructorul cu 2 argumente } } Dintr-o subclas˘ putem apela explicit constructorii superclasei cu expresia a super( argumente ). S˘ presupunem c˘ dorim s˘ cre˘m clasa Patrat, derivat˘ din clasa Dreptunghi: a a a a a class Patrat extends Dreptunghi { Patrat(double x, double y, double d) { super(x, y, d, d); // Apelam constructorul superclasei } } Atentie ¸ Apelul explcit al unui constructor nu poate 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 ıi creeaz˘ automat un constructor implicit, care nu prime¸te nici un argument a s ¸i care nu face nimic. Deci prezenta constructorilor ˆ corpul unei clase nu s ¸ ın este obligatorie. Dac˘ ˆ a scriem un constructor pentru o clas˘, care are mai a ıns˘ a mult de un argument, atunci constructorul implicit (f˘r˘ nici un argument) aa nu va mai fi furnizat implicit de c˘tre sistem. S˘ consider˘m, ca exemplu, a a a urm˘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 ¸ a implic˘ instantierea unui obiect din clasa p˘rinte. Din acest motiv, fiecare a ¸ a constructor 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 a expresia super([argumente]), ˆ caz contrar fiind semnalat˘ o eroare la ın a compilare.
  • 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: a public, protected, private ¸i cel implicit. s • public In orice alt˘ clas˘ se pot crea instante ale clasei respective. a a ¸ • protected Doar ˆ subclase pot 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 variabilelor Variabilele 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 ın unei metode, fiind vizibile ˆ toate metodele respectivei clase. Variabilele ın declarate ˆ 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 s Sunt variabile predefinite care fac referinta, ˆ cadrul unui obiect, la obiectul ¸ ın propriu-zis (this), respectiv la instanta p˘rintelui (super). Sunt folosite ¸ a ˆ general pentru a rezolva conflicte de nume prin referirea explicit˘ a unei ın a variabile sau metode membre. Dup˘ cum am v˘zut, utilizate sub form˘ de a a a metode au rolul de a apela constructorii corespunz˘tori ca argumente ai clasei a curente, respectiv ai superclasei class A { int x; A() { this(0); } A(int x) { this.x = x; } void metoda() { x ++; } } class B extends A { B() { this(0); } B(int x) { super(x); System.out.println(x); } void metoda() { super.metoda();
  • 51.
    50 CAPITOLUL 2. OBIECTE SI CLASE ¸ System.out.println(x); } } 2.3 Implementarea metodelor 2.3.1 Declararea metodelor Metodele sunt responsabile cu descrierea comportamentului unui obiect. In- trucˆt Java este un limbaj de programare complet orientat-obiect, metodele a se 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˘ a Metodele pot sau nu s˘ returneze o valoare la terminarea lor. Tipul returnat a poate fi atˆt un tip primitiv de date sau o referint˘ la un obiect al unei clase. a ¸a In cazul ˆ care o metod˘ nu returneaz˘ nimic atunci trebuie obligatoriu ın a a specificat 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 a intermediul instructiunii return, care trebuie s˘ apar˘ ˆ toate situatiile de ¸ a a ın ¸ terminare a functiei. ¸ double radical(double x) { if (x >= 0) return Math.sqrt(x); else { System.out.println("Argument negativ !"); // Eroare la compilare // Lipseste return pe aceasta ramura } } In cazul ˆ care ˆ declaratia functiei tipul returnat este un tip primitiv de ın ın ¸ ¸ date, valoarea returnat˘ la terminarea functiei trebuie s˘ aib˘ obligatoriu acel a ¸ a a tip sau un subtip al s˘u, altfel va fi furnizat˘ o eroare la compilare. In general, a a orice atribuire care implic˘ pierderi de date este tratat˘ de compilator ca a a eroare. int metoda() { return 1.2; // Eroare } int metoda() {
  • 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 ¸a clasa obiectului returnat trebuie s˘ coincid˘ sau s˘ fie o subclas˘ a clasei a a a a specificate la declararea metodei. De exemplu, fie clasa Poligon ¸i subclasa s acesteia Patrat. Poligon metoda1( ) { Poligon p = new Poligon(); Patrat t = new Patrat(); if (...) return p; // Corect else return t; // Corect } Patrat metoda2( ) { Poligon p = new Poligon(); Patrat t = new Patrat(); if (...) return p; // Eroare else return t; // Corect } 2.3.3 Trimiterea parametrilor c˘tre o metod˘ a a Signatura unei metode este dat˘ de numarul ¸i tipul argumentelor primite a s de acea metod˘. Tipul de date al unui argument poate fi orice tip valid al a limbajului 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 ın unei 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 ¸ a Pˆna la aparitia versiunii 1.5, ˆ Java o metod˘ nu putea primi un num˘r a ¸ ın a a variabil de argumente, ceea ce ˆ ınseamna c˘ apelul unei metode trebuia s˘ se a a fac˘ cu specificarea exact˘ a numarului ¸i tipurilor argumentelor. Vom anal- a a s iza ˆıntr-o sectiune separat˘ modalitate de specificare a unui num˘r variabil ¸ a a de argumente pentru o metod˘. a Numele argumentelor primite trebuie s˘ difere ˆ a ıntre ele ¸i nu trebuie s˘ co- s a incid˘ cu numele nici uneia din variabilele locale ale metodei. Pot ˆ a s˘ a ıns˘ a coincid˘ cu numele variabilelor membre ale clasei, caz ˆ care diferentierea a ın ¸ dintre ele se va face prin intermediul variabile this. class Cerc { int x, y, raza; public Cerc(int x, int y, int raza) { this.x = x; this.y = y; this.raza = raza; } } In Java argumentele sunt trimise doar prin valoare (pass-by-value). Acest lucru ˆınseamn˘ c˘ metoda receptioneaz˘ doar valorile variabilelor prim- a a ¸ a ite ca parametri. Cˆnd argumentul are tip primitiv de date, metoda nu-i poate schimba val- a oarea decˆt local (ˆ cadrul metodei); la revenirea din metod˘ variabila are a ın a aceea¸i valoare ca ˆ s ınaintea apelului, modific˘rile f˘cute ˆ cadrul metodei fi- a a ın ind pierdute. Cˆnd argumentul este de tip referint˘, metoda nu poate schimba valoarea a ¸a referintei obiectului, ˆ a poate apela metodele acelui obiect ¸i poate modifica ¸ ıns˘ s orice variabil˘ membr˘ accesibil˘. a a a A¸adar, dac˘ dorim ca o metod˘ s˘ schimbe starea (valoarea) unui argu- s a a a ment primit, atunci el trebuie s˘ fie neaparat de tip referint˘. a ¸a
  • 56.
    2.3. IMPLEMENTAREA METODELOR 55 De exemplu, s˘ consider˘m clasa Cerc descris˘ anterior ˆ care dorim s˘ a a a ın a implement˘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 s valorile 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 a nu realizeaz˘ nimic pentru c˘ nu poate schimba valorile variabilelor primite a a ca argumente. Pentru a rezolva lucrul propus trebuie s˘ definim o clas˘ suplimentar˘ a a a care s˘ descrie parametrii pe care dorim s˘-i afl˘m: a a a // Varianta corecta class Param { public int x, y, raza; } class Cerc { private int x, y, raza; public void aflaParametri(Param param) { param.x = x; param.y = y; param.raza = raza; } } Argumentul param are tip referint˘ ¸i, de¸i nu ˆ schimb˘m valoarea (val- ¸a s s ıi a oarea 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 ¸ a acesta. Varianta de mai sus a fost dat˘ pentru a clarifica modul de trimitere a a argumentelor unei metode. Pentru a afla ˆ a valorile variabilelor care descriu ıns˘ starea unui obiect se folosesc metode de tip getter ˆ ¸ite de metode setter ınsot care s˘ permit˘ schimbarea st˘rii obiectului: a a a class Cerc { private int x, y, raza; public int getX() { return x; } public void setX(int x) { this.x = x; } ... } 2.3.4 Metode cu num˘r variabil de argumente a Incepˆnd cu versiunea 1.5 a limbajului Java, exist˘ posibilitate de a declara a a metode 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 ¸ a variabil de argumente, ˆ functie de apelul metodei. Tipul argumentelor ın ¸ poate fi referint˘ sau primitiv. Metoda de mai jos afi¸eaz˘ argumentele prim- ¸a s a ite, 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 57 2.3.5 Supraˆ arcarea ¸i supradefinirea metodelor ınc˘ s Supraˆ arcarea ¸i supradefinirea metodelor sunt dou˘ concepte extrem de ınc˘ s a utile ale program˘rii orientate obiect, cunoscute ¸i sub denumirea de polimor- a s fism, ¸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. a class A { void metoda() { System.out.println("A: metoda fara parametru"); } // Supraincarcare void metoda(int arg) { System.out.println("A: metoda cu un parametru"); } } class B extends A { // Supradefinire void metoda() { System.out.println("B: metoda fara parametru"); } } O metod˘ 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 a metodele abstracte ale superclasei (dac˘ este cazul). In cazul ˆ care o clas˘ a ın a nu supradefine¸te toate metodele abstracte ale p˘rintelui, ea ˆ a¸i este ab- s a ıns˘s stract˘ ¸i va trebui declarat˘ ca atare. as a In Java nu este posibil˘ supraˆ arcarea operatorilor. a ınc˘ 2.4 Modificatori de acces Modificatorii de acces sunt cuvinte rezervate ce controleaz˘ accesul celor- a late clase la membrii unei clase. Specificatorii de acces pentru variabilele ¸i metodele unei clase sunt: public, protected, private ¸i cel implicit (la s s nivel de pachet), iar nivelul lor de acces este dat ˆ tabelul de mai jos: ın 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 a nivelul de acces este la nivelul pachetului. In cazul ˆ care declar˘m un ın a membru ”protected” atunci accesul la acel membru este permis din subclasele clasei ˆ care a fost declarat dar depinde ¸i de pachetul ˆ care se gase¸te ın s ın s subclasa: dac˘ sunt ˆ acela¸i pachet accesul este permis, dac˘ nu sunt ˆ a ın s a ın acela¸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 a O 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. a 2.5.1 Variabile de instant˘ ¸i de clas˘ ¸a s a Cˆnd declar˘m o variabil˘ membr˘ f˘r˘ modificatorul static, cum ar fi x ˆ a a a a aa ın exemplul de mai jos: class Exemplu { int x ; //variabila de instanta } se declar˘ de fapt o variabil˘ de instant˘, ceea ce ˆ a a ¸a ınseamn˘ c˘ la fiecare creare a a a unui obiect al clasei Exemplu sistemul aloc˘ o zon˘ de memorie separat˘ a a a pentru memorarea valorii lui x. Exemplu o1 = new Exemplu(); o1.x = 100;
  • 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 s variabilele sale de instant˘. ¸a Pentru variabilele de clas˘ (statice) sistemul aloc˘ o singur˘ zon˘ de mem- a a a a orie la care au acces toate instantele clasei respective, ceea ce ˆ ¸ ınseamn˘ c˘ a a dac˘ un obiect modific˘ valoarea unei variabile statice ea se va modifica ¸i a a s pentru toate celelalte obiecte. Deoarece nu depind de o anumit˘ instanta a a ¸˘ unei clase, variabilele statice pot fi referite ¸i sub forma: s NumeClasa.numeVariabilaStatica class 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˘ ın memorie a clasei respective, ¸i este realizat˘ prin atribuiri obi¸nuite: s a s class 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 61 2.5.2 Metode de instant˘ ¸i de clas˘ ¸a s a Similar ca la variabile, metodele declarate f˘r˘ modificatorul static sunt aa metode de instant˘ iar cele declarate cu static sunt metode de clas˘ (stat- ¸a a ice). 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 a de starea obiectelor clasei respective, apelul lor se poate face ¸i sub forma: s NumeClasa.numeMetodaStatica Exemplu.metodaStatica(); // Corect, echivalent cu Exemplu obj = new Exemplu(); obj.metodaStatica(); // Corect, de asemenea Metodele de instant˘ nu pot fi apelate decˆt pentru un obiect al clasei ¸a a respective: 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˘ a Membrii de clas˘ sunt folositi pentru a pune la dispozitie valori ¸i metode a ¸ ¸ s independente de starea obiectelor dintr-o anumita clas˘. a Declararea eficient˘ a constantelor a S˘ consider˘m situatia cˆnd dorim s˘ declar˘m o constant˘. a a ¸ a a a a class Exemplu { final double PI = 3.14; // Variabila finala de instanta } La fiecare instantiere a clasei Exemplu va fi rezervat˘ o zon˘ de memorie ¸ a a pentru variabilele finale ale obiectului respectiv, ceea ce este o risip˘ ˆ a ıntrucˆt a aceste 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 s pentru a le rezerva o singur˘ zon˘ de memorie, comun˘ tuturor obiectelor: a a a class Exemplu { static final double PI = 3.14; // Variabila finala de clasa } Num˘rarea obiectelor unei clase a Num˘rarea obiectelor unei clase poate fi f˘cut˘ extrem de simplu folosind a a a o variabil˘ static˘ ¸i este util˘ ˆ situatiile cˆnd trebuie s˘ control˘m diver¸i a as a ın ¸ a a a s parametri legati de crearea obiectelor unei clase. ¸ class Exemplu { static long nrInstante = 0; Exemplu() { // Constructorul este apelat la fiecare instantiere nrInstante ++; } }
  • 64.
    ¸˘ ¸ ˘ 2.5. MEMBRI DE INSTANTA SI MEMBRI DE CLASA 63 Implementarea functiilor globale ¸ Spre deosebire de limbajele de programare procedurale, ˆ Java nu putem ın avea functii globale definite ca atare, ˆ ¸ ıntrucˆt ”orice este un obiect”. Din a acest motiv chiar ¸i metodele care au o functionalitate global˘ trebuie im- s ¸ a plementate ˆ cadrul unor clase. Acest lucru se va face prin intermediul ın metodelor de clas˘ (globale), deoarece acestea nu depind de starea partic- a ular˘ 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 a nu ar fi fost functie de clas˘, apelul ei ar fi trebuit f˘cut astfel (incorect, de ¸ a a altfel): // 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 a apelat˘ prin: Math.sqrt(121) . a A¸adar, functiile globale necesare unei aplicatii vor fi grupate corespunz˘tor s ¸ ¸ a ˆ diverse clase ¸i implementate ca metode statice. ın s 2.5.4 Blocuri statice de initializare ¸ Variabilele statice ale unei clase sunt initializate la un moment care precede ¸ prima utilizare activ˘ a clasei respective. Momentul efectiv depinde de im- a plementarea 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 s blocurile statice de initializare ale clasei. Acestea sunt secvente de cod de ¸ ¸ forma: static { // Bloc static de initializare; ... } care se comport˘ ca o metod˘ static˘ apelat˘ automat de c˘tre ma¸ina vir- a a a a a s tual˘. Variabilele referite ˆ a ıntr-un bloc static de initializare trebuie s˘ fie ¸ a obligatoriu de clas˘ sau locale blocului: a public 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 imbricate 2.6.1 Definirea claselor imbricate O 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 se s a ¸ ¸ poate face fie ca membru al clasei de acoperire - caz ˆ care este accesibil˘ ın a tuturor metodelor, fie local ˆ cadrul unei metode. ın class 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 ın plementarea ei de o alt˘ clas˘ ¸i nu exist˘ nici un motiv pentru care aceasta a as a din 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 s acces nerestrictionat la toate variabilele clasei de acoperire, chiar dac˘ aces- ¸ a tea sunt private. O clas˘ declarat˘ local˘ unei metode va avea acces ¸i la a a a s variabilele finale declarate ˆ metoda respectiv˘. ın a class ClasaDeAcoperire{ private int x=1; class ClasaImbricata1 { int a=x; } void metoda() { final int y=2; int z=3; class ClasaImbricata2 { int b=x; int c=y; int d=z; // Incorect } } } O clas˘ imbricat˘ membr˘ (care nu este local˘ unei metode) poate fi a a a a referit˘ din exteriorul clasei de acoperire folosind expresia a ClasaDeAcoperire.ClasaImbricata A¸adar, clasele membru pot fi declarate cu modificatorii public, protected, s private pentru a controla nivelul lor de acces din exterior, ˆ ıntocmai ca orice variabil˘ sau metod˘ mebr˘ a clasei. Pentru clasele imbricate locale unei a a a metode nu sunt permi¸i acesti modificatori. s ¸ Toate clasele imbricate pot fi declarate folosind modificatorii abstract ¸i s final, 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 interne Spre deosebire de clasele obi¸nuite, o clas˘ imbricat˘ poate fi declarat˘ static˘ s a a a a sau nu. O clas˘ imbricat˘ nestatic˘ se nume¸te clasa intern˘. a a a s a class 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 ¸ s la toti membrii clasei sale de acoperire. ¸ 2.6.3 Identificare claselor imbricate Dup˘ cum ¸tim orice clas˘ produce la compilare a¸a numitele ”unit˘¸i de com- a s a s at pilare”, 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 ¸ ¸ a bricate aceste unit˘¸i de compilare sunt denumite astfel: numele clasei de at acoperire, urmat de simbolul ’$’ apoi de numele clasei imbricate. class ClasaDeAcoperire{ class ClasaInterna1 {} class ClasaInterna2 {} }
  • 68.
    2.7. CLASE SIMETODE 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 a unui ’$’ ¸i apoi numele clasei imbricate. s 2.6.4 Clase anonime Exist˘ posibilitatea definirii unor clase imbricate locale, f˘r˘ nume, utilizate a aa doar pentru instantierea unui obiect de un anumit tip. Astfel de clase se ¸ numesc clase anonime ¸i sunt foarte utile ˆ situatii cum ar fi crearea unor s ın ¸ obiecte ce implementeaz˘ o anumit˘ interfat˘ sau extind o anumit˘ clas˘ a a ¸a a a abstract˘. a Exemple de folosire a claselor anonime vor 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 a de forma ClasaAcoperire.$1,..., ClasaAcoperire.$n, unde n este num˘rul a de clase anonime definite ˆ clasa respectiv˘ de acoperire. ın a 2.7 Clase ¸i metode abstracte s Uneori ˆ proiectarea unei aplicatii este necesar s˘ reprezent˘m cu ajutorul ın ¸ a a claselor concepte abstracte care s˘ nu poat˘ fi instantiate ¸i care s˘ foloseasc˘ a a ¸ s a a doar la dezvoltarea ulterioar˘ a unor clase ce descriu obiecte concrete. De ex- a emplu, ˆ pachetul java.lang exist˘ clasa abstract˘ Number care modeleaz˘ ın a a a conceptul 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 Number serve¸te ca superclas˘ pentru clasele concrete Byte, Double, Float, Integer, s a Long ¸i Short, ce implementeaz˘ obiecte pentru descrierea numerelor de un s a anumit tip. A¸adar, clasa Number reprezint˘ un concept abstract ¸i nu vom s a s putea instantia obiecte de acest tip - vom folosi ˆ schimb subclasele sale. ¸ ın Number numar = new Number(); // Eroare Integer intreg = new Integer(10); // Corect
  • 69.
    68 CAPITOLUL 2. OBIECTE SI CLASE ¸ 2.7.1 Declararea unei clase abstracte Declararea 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 a la nivel de pachet, dar nu poate specifica modificatorul final, combinatia ¸ abstract final fiind semnalat˘ ca eroare la compilare - de altfel, o clas˘ a a declarat˘ astfel nu ar avea nici o utilitate. a O clas˘ abstract˘ poate contine acelea¸i elemente membre ca o clas˘ a a ¸ s a obi¸nuit˘, la care se adaug˘ declaratii de metode abstracte - f˘r˘ nici o im- s a a ¸ aa plementare. 2.7.2 Metode abstracte Spre deosebire de clasele obi¸nuite care trebuie s˘ furnizeze implement˘ri s a a pentru toate metodele declarate, o clas˘ abstract˘ poate contine metode f˘r˘ a a ¸ aa nici o implementare. Metodele fara nici o implementare se numesc metode abstracte ¸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 a eroare de compilare. abstract class ClasaAbstracta { abstract void metodaAbstracta(); // Corect void metoda(); // Eroare } In felul acesta, o clas˘ abstract˘ poate pune la dispozitia subclaselor sale a a ¸ un model complet pe care trebuie s˘-l implementeze, furnizˆnd chiar imple- a a mentarea unor metode comune tuturor claselor ¸i l˘sˆnd explicitarea altora s aa
  • 70.
    2.7. CLASE SIMETODE ABSTRACTE ¸ 69 fiec˘rei subclase ˆ parte. a ın Un exemplu elocvent de folosire a claselor ¸i metodelor abstracte este de- s scrierea 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 ın tajul nostru putem declara o clas˘ generic˘ GraphicObject care s˘ fie su- a a a perclas˘ pentru celelalte clase. Metodele abstracte vor fi folosite pentru im- a plementarea comportamentului specific fiec˘rui obiect, cum ar fi desenarea a iar cele obi¸nuite pentru comportamentul comun tuturor, cum ar fi schim- s barea originii. Implementarea clasei abstracte GraphicObject ar putea ar˘ta a astfel: abstract class GraphicObject { // Stari comune private int x, y; private Color color = Color.black; ... // Metode comune public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setColor(Color color) { this.color = color; } ... // Metode abstracte abstract void draw(); ... }
  • 71.
    70 CAPITOLUL 2. OBIECTE SI CLASE ¸ O subclas˘ care nu este abstract˘ a unei clase abstracte trebuie s˘ furnizeze a a a obligatoriu implement˘ri ale metodelor abstracte definite ˆ superclas˘. Im- a ın a plementarea 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 de ierarhii care folosesc la nivelele superioare clase abstracte. Dintre cele mai importante amintim: • Number: superclasa abstract˘ a tipurilor referint˘ numerice a ¸a • Reader, Writer: superclasele abstracte ale 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 Object 2.8.1 Orice clas˘ are o superclas˘ a a Dup˘ cum am v˘zut ˆ sectiunea dedicat˘ modalit˘¸ii de creare a unei clase, a a ın ¸ a at clauza ”extends” specific˘ faptul c˘ acea clas˘ extinde (mo¸tene¸te) o alt˘ a a a s s a clas˘, numit˘ superclas˘. O clas˘ poate avea o singur˘ superclas˘ (Java nu a a a a a a suport˘ mo¸tenirea multipl˘) ¸i chiar dac˘ nu specific˘m clauza ”extends” la a s a s a a crearea unei clase ea totu¸i va avea o superclas˘. Cu alte cuvinte, ˆ Java s a ın orice clas˘ are o superclas˘ ¸i numai una. Evident, trebuie s˘ existe o exceptie a as a ¸ de la aceast˘ regul˘ ¸i anume clasa care reprezint˘ r˘d˘cina ierarhiei format˘ a as a a a a de 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 a anumit˘ superclas˘. Declaratiile de mai jos sunt echivalente: a a ¸ class Exemplu {} class Exemplu extends Object {} 2.8.2 Clasa Object Clasa Object este cea mai general˘ dintre clase, orice obiect fiind, direct a sau indirect, descendent al acestei clase. Fiind p˘rintele tuturor, Object a define¸te ¸i implementeaz˘ comportamentul comun al tuturor celorlalte clase s s a Java, 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 ıi care 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 ın De asemenea, vom scrie un mic program TestComplex ˆ care vom testa ın metodele clasei definite. Listing 2.1: Clasa numerelor complexe class Complex { private double a ; // partea reala private double b ; // partea imaginara public Complex ( double a , double b ) { this . a = a ; this . b = b ; } public Complex () { this (1 , 0) ; } public boolean equals ( Object obj ) { if ( obj == null ) return false ; if (!( obj instanceof Complex ) ) return false ; Complex comp = ( Complex ) obj ; return ( comp . a == a && comp . b == b ) ; } public Object clone () { return new Complex (a , b ) ; } public String toString () { String semn = ( b > 0 ? " + " : " -" ) ; return a + semn + b + " i " ; } public Complex aduna ( Complex comp ) { Complex suma = new Complex (0 , 0) ; suma . a = this . a + comp . a ; suma . b = this . b + comp . b ; return suma ; } }
  • 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 tipuri Dup˘ cum v˘zut tipurile Java de date pot fi ˆ artie ˆ primitive ¸i referint˘. a a ımp˘ ¸ ın s ¸a Pentru fiecare tip primitiv exist˘ o clas˘ corespunz˘toare care permie lucrul a a a orientat 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 as versia 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 ıntre tipuri primitve ¸i referint˘ sunt posibile, acest mecanism purtˆnd numele de s ¸a a autoboxing, respectiv auto-unboxing. Conversia explicit˘ va fi facut˘ de c˘tre a a a compilator.
  • 76.
    2.10. TIPUL DEDATE 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 enumerare Incepˆnd cu versiunea 1.5 a limbajului Java, exist˘ posibilitatea de a defini a a tipuri de date enumerare prin folosirea cuvˆntului cheie enum. Acest˘ a a solutie simplific˘ manevrarea grupurilor de constante, dup˘ cum reiese din ¸ a a urm˘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 3 Exceptii ¸ 3.1 Ce sunt exceptiile ? ¸ Termenul exceptie este o prescurtare pentru ”eveniment exceptional” ¸i poate ¸ ¸ s fi definit ca un eveniment ce se produce ˆ timpul executiei unui program ¸i ın ¸ s care provoac˘ ˆ a ıntreruperea cursului normal al executiei acestuia. ¸ Exceptiile pot ap˘rea din diverse cauze ¸i pot avea nivele diferite de grav- ¸ a s itate: 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 afara t spatiului 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 s genul: 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 a o 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 a metoda care a generat exceptia ¸i mergˆnd ˆ ¸ s a ınapoi pe linia apelurilor c˘trea acea 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 ¸ s ei 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 s analizor pentru o anumit˘ exceptie, atunci programul Java se opre¸te cu un a ¸ s mesaj de eroare (ˆ cazul exemplului de mai sus mesajul ”Aici nu se mai ın ajunge...” nu va fi afi¸at). s Atentie ¸ In Java tratarea erorilor nu mai este o optiune ci o constrˆngere. In ¸ a aproape toate situatile, o secvent˘ de cod care poate provoca exceptii trebuie ¸ ¸a ¸ s˘ specifice modalitatea de tratare a acestora. a 3.2 ”Prinderea” ¸i tratarea exceptiilor s ¸ Tratarea exceptiilor se realizeaz˘ prin intermediul blocurilor de instructiuni ¸ a ¸ try, catch ¸i finally. O secvent˘ de cod care trateaz˘ anumite exceptii s ¸a a ¸ trebuie s˘ arate astfel: a try { // Instructiuni care pot genera exceptii } catch (TipExceptie1 variabila) { // Tratarea exceptiilor de tipul 1
  • 80.
    3.2. ”PRINDEREA” SITRATAREA 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 s afisarea lui pe ecran. F˘r˘ a folosi tratarea exceptiilor metoda responsabil˘ aa ¸ a cu 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 ın tratarea erorilor este obligatorie. Folosind mecanismul exceptiilor metoda ¸ citeste ˆsi poate trata singur˘ erorile care pot surveni pe parcursul executiei ı¸ a ¸ sale. Mai jos este codul complte ¸i corect al unui program ce afi¸eaz˘ pe ecran s s a continutul unui fi¸ier al c˘rui nume este primit ca argument de la linia de ¸ s a comand˘. Tratarea exceptiilor este realizat˘ complet chiar de c˘tre metoda a ¸ a a citeste.
  • 81.
    80 CAPITOLUL 3. EXCEPTII ¸ Listing 3.1: Citirea unui fisier - corect import java . io .*; public class CitireFisier { public static void citesteFisier ( String fis ) { FileReader f = null ; try { // Deschidem fisierul System . out . println ( " Deschidem fisierul " + fis ) ; f = new FileReader ( fis ) ; // Citim si afisam fisierul caracter cu caracter int c ; while ( ( c = f . read () ) != -1) System . out . print (( char ) c ) ; } catch ( File Not F o u n d E x c e p t i o n e ) { // Tratam un tip de exceptie System . err . println ( " Fisierul nu a fost gasit ! " ) ; System . err . println ( " Exceptie : " + e . getMessage () ) ; System . exit (1) ; } catch ( IOException e ) { // Tratam alt tip de exceptie System . out . println ( " Eroare la citirea din fisier ! " ) ; e . printStackTrace () ; } finally { if ( f != null ) { // Inchidem fisierul System . out . println ( " nInchidem fisierul . " ) ; try { f . close () ; } catch ( IOException e ) { System . err . println ( " Fisierul nu poate fi inchis ! " ) ; e . printStackTrace () ; } } } } public static void main ( String args []) { if ( args . length > 0) citesteFisier ( args [0]) ; else
  • 82.
    3.2. ”PRINDEREA” SITRATAREA EXCEPTIILOR ¸ ¸ 81 System . out . println ( " Lipseste numele fisierului ! " ) ; } } Blocul ”try” contine instructiunile de deschidere a unui fi¸ier ¸i de citire ¸ s s dintr-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 a tru fiecare tip de exceptie. Inchiderea fi¸ierului se face ˆ blocul ”finally”, ¸ s ın deoarece 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 s fost 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 s la rˆndul s˘u exceptii, de exemplu atunci cˆnd fi¸ierul mai este folosit ¸i de a a ¸ a s s alt proces ¸i nu poate fi ˆ s ınchis. Deci, pentru a avea un cod complet corect trebuie 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 ¸ a sau 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 a mai 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 ¸ a leaz˘, 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 ¸ ın metodei citeste atunci metoda apelant˘ (main) va trebui s˘ fac˘ acest lucru: a a a Listing 3.2: Citirea unui fisier import java . io .*; public class CitireFisier { public static void citesteFisier ( String fis ) throws FileNotFoundException , IOException { FileReader f = null ; f = new FileReader ( fis ) ; int c ; while ( ( c = f . read () ) != -1) System . out . print (( char ) c ) ; f . close () ; }
  • 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 s De asemenea, inchiderea fi¸ierului nu va mai fi facut˘ ˆ situatia ˆ care s a ın ın apare o exceptie la citirea din fi¸ier. Este situatia ˆ care putem folosi blocul ¸ s ¸ ın finally 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 ¸ a metoda care a apelat-o la rˆndul ei. Aceast˘ ˆ antuire se termin˘ cu metoda a a ınl˘ ¸ a main care, dac˘ va arunca exceptiile ce pot ap˘rea ˆ corpul ei, va determina a ¸ a ın trimiterea 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 s afi¸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 clar s 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 TRATARIIEXCEPTIILOR ¸ 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 ¸ a fat˘ de mecanismul traditional de tratare a erorilor: ¸a ¸ • Separarea codului pentru tratarea unei erori de codul ˆ care ea poate ın s˘ apar˘. a a • Propagarea unei erori pˆn˘ la un analizor de exceptii corespunz˘tor. a a ¸ a • Gruparea erorilor dup˘ tipul lor. a 3.4.1 Separarea codului pentru tratarea erorilor In programarea traditional˘ tratarea erorilor se combin˘ cu codul ce poate ¸ a a produce aparitia lor producˆnd a¸a numitul ”cod spaghetti”. S˘ consider˘m ¸ a s a a urm˘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 ¸ a genul: ”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˘ a extrem de mult ˆ ¸telegerea sa. In Java, folosind mecansimul exceptiilor, ınt ¸ codul ar arata, schematizat, astfel:
  • 88.
    ˘ 3.4. AVANTAJELE TRATARIIEXCEPTIILOR ¸ 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˘. a 3.4.2 Propagarea erorilor Propagarea unei erori se face pˆn˘ la un analizor de exceptii corespunz˘tor. a a ¸ a S˘ presupunem c˘ apelul la metoda citesteFisier este consecinta unor a a ¸ apeluri imbricate de metode: int metoda1() { metoda2(); ... } int metoda2() { metoda3; ... } int metoda3 { citesteFisier(); ...
  • 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 ¸ a produse 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 ¸ a sau 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 TRATARIIEXCEPTIILOR ¸ 89 int metoda2() throws TipExceptie { metoda3(); ... } int metoda3() throws TipExceptie { citesteFisier(); ... } 3.4.3 Gruparea erorilor dup˘ tipul lor a In Java exist˘ clase corespunz˘toare tuturor exceptiilor care pot ap˘rea la a a ¸ a executia unui program. Acestea sunt grupate ˆ functie de similarit˘¸ile ¸ ın ¸ at lor ˆıntr-o ierarhie de clase. De exemplu, clasa IOException se ocup˘ cu a exceptiile ce pot ap˘rea la operatii de intrare/iesire ¸i diferentiaz˘ la rˆndul ¸ a ¸ s ¸ a a ei 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 a exceptii ¸i anume clasa Exception. ¸ s Radacin˘ acestei ierarhii este clasa Throwable (vezi ”Ierarhia claselor ce de- a scriu 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 a tratarea 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 s sunt 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 a gram Java ¸i sunt invizibile programatorilor. Un program Java nu trebuie s˘ s a trateze aparitia acestor erori ¸i este improbabil ca o metod˘ Java s˘ provoace ¸ s a a asemenea erori.
  • 92.
    3.6. EXCEPTII LAEXECUTIE ¸ ¸ 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 a tor exceptii nu este o optiune ci o constrˆngere. Exceptiile care pot ”sc˘pa” ¸ ¸ a ¸ a netratate 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 ¸ ın sustrag ˆ 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 a situatie extern˘, cum ar fi lipsa unui fi¸ier. ¸ a s Aceste exceptii au o superclas˘ comun˘ RuntimeException ¸i ˆ acesata ¸ a a s ın categorie sunt incluse exceptiile provocate de: ¸ • operatii aritmetice ilegale (ˆ artirea ˆ ¸ ımpˆ ¸ ıntregilor la zero); ArithmeticException • accesarea membrilor unui obiect ce are valoarea null; NullPointerException • accesarea eronat˘ a elementelor unui vector. a ArrayIndexOutOfBoundsException Exceptiile la executie pot ap˘rea uriunde ˆ program ¸i pot fi extrem ¸ ¸ a ın s de numeroare iar ˆ ıncercarea de ”prindere” a lor ar fi extrem de anevoioas˘. a Din acest motiv, compilatorul permite ca aceste exceptii s˘ r˘mˆn˘ netratate, ¸ a a a a tratarea lor nefiind ˆ a ilegal˘. Reamintim ˆ a c˘, ˆ cazul aparitiei oric˘rui ıns˘ a ıns˘ a ın ¸ a tip de exceptie care nu are un analizor corespunz˘tor, programul va fi termi- ¸ a nat.
  • 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 s generat˘ nici o exceptie, ci va fi furnizat ca rezultat o constant˘ care poate a ¸ a fi, functie de operatie, Infinity, -Infinity, sau Nan. ¸ int a=1, int b=0; System.out.println(a/b); // Exceptie la executie ! double x=1, y=-1, z=0; System.out.println(x/z); // Infinity System.out.println(y/z); // -Infinity System.out.println(z/z); // NaN 3.7 Crearea propriilor exceptii ¸ Adeseori poate 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 ¸ a cazuri care nu au fost prevazute ˆ ierarhia exceptiilor standard Java. ın ¸ O exceptie proprie trebuie s˘ se ˆ ¸ a ıncadreze ˆ a ˆ ierarhia exceptiilor Java, ıns˘ ın ¸ cu alte cuvinte clasa care o implementeaz˘ trebuie s˘ fie subclas˘ a uneia a a a deja existente ˆ aceasta ierarhie, preferabil una apropiat˘ ca semnificatie, ın a ¸ sau superclasa Exception.
  • 94.
    3.7. CREAREA PROPRIILOREXCEPTII ¸ 93 public 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- ¸ a tiv de scoatere a elementului din vˆrful stivei. Dac˘ presupunem c˘ stiva a a a poate memora maxim 100 de elemente, ambele operatii pot provoca exceptii. ¸ ¸ Pentru a personaliza aceste exceptii vom crea o clas˘ specific˘ denumit˘ ¸ a a a ExceptieStiva: Listing 3.3: Exceptii proprii class ExceptieStiva extends Exception { public ExceptieStiva ( String mesaj ) { super ( mesaj ) ; } } class Stiva { int elemente [] = new int [100]; int n =0; // numarul de elemente din stiva public void adauga ( int x ) throws ExceptieStiva { if ( n ==100) throw new ExceptieStiva ( " Stiva este plina ! " ) ; elemente [ n ++] = x ; } public int scoate () throws ExceptieStiva { if ( n ==0) throw new ExceptieStiva ( " Stiva este goala ! " ) ; return elemente [n - -]; } } Secventa cheie este extends Exception care specific˘ faptul c˘ noua ¸ a a clas˘ ExceptieStiva este subclas˘ a clasei Exception ¸i deci implementeaz˘ a a s a obiecte 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 s Procesul de creare a unei noi exceptii poate fi dus mai departe prin ad˘ugarea ¸ a unor noi metode clasei ce descrie acea exceptie, ˆ a aceasta dezvoltare nu ¸ ıns˘ ˆsi are rostul ˆ majoritatea cazurilor. Exceptiile proprii sunt descrise uzual ı¸ ın ¸ de clase foarte simple, chiar 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 4 Intr˘ri ¸iie¸iri a s s 4.1 Introducere 4.1.1 Ce sunt fluxurile? Majoritatea aplicatiilor necesit˘ citirea unor informatii care se g˘sesc pe ¸ a ¸ a o surs˘ extern˘ sau trimiterea unor informatii c˘tre o destinatie extern˘. a a ¸ a ¸ a Informatia se poate g˘si oriunde: ˆ ¸ a ıntr-un fi¸ier pe disc, ˆ retea, ˆ memorie s ın ¸ ın sau ˆ alt program ¸i poate fi de orice tip: date primitive, obiecte, imagini, ın s sunete, etc. Pentru a aduce informatii dintr-un mediu extern, un progam Java ¸ trebuie s˘ deschid˘ un canal de comunicatie (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 ¸ a deschizˆnd un canal de comunicatie (flux) c˘tre acea destinatie ¸i scriind a ¸ a ¸ s secvential informatiile respective. ¸ ¸ Indiferent de tipul informatiilor, citirea/scrierea de pe/c˘tre un mediu ¸ a extern 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. a Un proces care descrie o surs˘ extern˘ de date se nume¸te proces produc˘tor. a a s a Un proces care descrie o destinatie extern˘ pentru date se nume¸te proces ¸ a s consumator. Un flux care cite¸te date se nume¸te flux de intrare. s s Un 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. ¸ a Fiecare flux are un singur proces produc˘tor ¸i un singur proces consumator. a s Intre dou˘ procese pot exista oricˆte fluxuri, orice proces putˆnd fi atˆt pro- a a a a ducator cˆt ¸i consumator ˆ acela¸i timp, dar pe fluxuri diferite. a s ın s Consumatorul ¸i producatorul nu comunic˘ direct printr-o interfat˘ de flux s a ¸a ci prin intermediul codului Java de tratare a fluxurilor. Clasele ¸i intefetele standard pentru lucrul cu fluxuri se g˘sesc ˆ pachetul s ¸ a ın java.io. Deci, orice program care necesit˘ operatii de intrare sau ie¸ire tre- a ¸ s buie s˘ contin˘ instructiunea de import a pachetului java.io: a ¸ a ¸ import java.io.*; 4.1.2 Clasificarea fluxurilor Exist˘ 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 datelor 4.1.3 Ierarhia claselor pentru lucrul cu fluxuri Clasele 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. s Acestea sunt superclase abstracte pentru toate clasele ce implementeaz˘ a fluxuri 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 ¸ ın BufferedReader, 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 ıi corespunde 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 a specializate pentru citirea/scrierea datelor pe 8 biti. Ca ¸i ˆ cazul flux- ¸ s ın urilor pe caractere denumirile claselor vor avea terminatia superclasei lor: ¸ FileInputStream, BufferedInputStream, FileOutputStream, BufferedOutputStream, etc., fiec˘rui flux de intrare XInputStream core- a spunzˆndu-i uzual un flux de ie¸ire XOutputStream, f˘r˘ ca acest lucru s˘ a s aa a fie 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 a mediul 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 fluxurilor Superclasele abstracte Reader ¸i InputStream definesc metode similare pen- s tru citirea datelor. Reader InputStream int read() int read() int read(char buf[]) int read(byte buf[]) ... ... De asemenea, ambele clase pun la dispozitie metode pentru marcarea ¸ unei locatii ˆ ¸ ıntr-un 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, s definind 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 ın aceasta nu este apelat˘ explicit, fluxul va fi automat ˆ a ınchis de c˘tre colectorul a de 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, a datele 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 99 4.2 Folosirea fluxurilor A¸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 s procesarea datelor (de filtrare). In continuare, vom vedea care sunt cele mai importante clase din cele dou˘ categorii ¸i la ce folosesc acestea, precum ¸i a s s modalit˘¸ile de creare ¸i utilizare a fluxurilor. at s 4.2.1 Fluxuri primitive Fluxurile primitive sunt responsabile cu citirea/scrierea efectiv˘ a datelor, a punˆnd la dispozitie implement˘ri ale metodelor de baz˘ read, respectiv a ¸ a a write, 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 procesare Fluxurile de procesare (sau de filtrare) sunt responsabile cu preluarea datelor de la un flux primitiv ¸i procesarea acestora pentru a le oferi ˆ s ıntr-o alt˘ form˘, a a mai util˘ dintr-un anumit punct de vedere. De exemplu, BufferedReader a poate prelua date de la un 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 a cu caracter. Un flux de procesare nu poate fi folosit decˆt ˆ a ımpreun˘ cu un a flux 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 flux Orice flux este un obiect al clasei ce implementeaz˘ fluxul respectiv. Crearea a unui 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 aa un flux primitiv de citire/scriere a datelor. Din acest motiv, constructorii claselor pentru fluxurile de procesare nu primesc ca argument un dispozitiv extern de memorare a datelor ci o referinta la un flux primitiv responsabil ¸ cu citirea/scrierea efectiv˘ a datelor: a Exemple: //crearea unui flux de intrare printr-un buffer BufferedReader in = new BufferedReader( new FileReader("fisier.txt")); //echivalent cu FileReader fr = new FileReader("fisier.txt"); BufferedReader in = new BufferedReader(fr); //crearea unui flux de iesire printr-un buffer BufferedWriter out = new BufferedWriter( new FileWriter("fisier.txt"))); //echivalent cu FileWriter fo = new FileWriter("fisier.txt"); BufferedWriter out = new BufferedWriter(fo); 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 s Fluxurile pentru lucrul cu fi¸iere sunt cele mai usor de ˆ s ınteles, ˆ ıntrucˆt a operatia lor de baz˘ este citirea, respectiv scrierea unui caracter sau octet ¸ a dintr-un sau ˆıntr-un fi¸ier specificat uzual prin numele s˘u complet sau relativ s a la directorul curent. Dup˘ cum am v˘zut deja, clasele care implementeaz˘ aceste fluxuri sunt a a a urm˘toarele: a FileReader, FileWriter - caractere FileInputStream, FileOutputStream - octeti Constructorii acestor clase accept˘ ca argument un obiect care s˘ specifice a a un anume fi¸ier. Acesta poate fi un ¸ir de caractere, on obiect de tip File s s sau un obiect de tip FileDesciptor (vezi ”Clasa File”). Constructorii clasei FileReader sunt: public FileReader(String fileName) throws FileNotFoundException public FileReader(File file) throws FileNotFoundException public FileReader(FileDescriptor fd) Constructorii clasei FileWriter: public FileWriter(String fileName) throws IOException public FileWriter(File file) throws IOException public FileWriter(FileDescriptor fd) public FileWriter(String fileName, boolean append) throws IOException
  • 105.
    104 ˘ CAPITOLUL 4. INTRARI SI IESIRI ¸ ¸ Cei mai uzuali constructori sunt cei care primesc ca argument numele fi¸ierului. Ace¸tia pot provoca exceptii de tipul FileNotFoundException ˆ s s ¸ ın cazul ˆ care fi¸ierul cu numele specificat nu exist˘. Din acest motiv orice ın s a creare a unui flux de acest tip trebuie f˘cut˘ ˆ a a ıntr-un bloc try-catch sau metoda ˆ 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 ¸ s cu numele ”in.txt” ˆ ıntr-un alt fi¸ier cu numele ”out.txt”. Ambele fi¸iere sunt s s considerate ˆ directorul curent. ın Listing 4.1: Copierea unui fisier import java . io .*; public class Copiere { public static void main ( String [] args ) { try { FileReader in = new FileReader ( " in . txt " ) ; FileWriter out = new FileWriter ( " out . txt " ) ; int c ; while (( c = in . read () ) != -1) out . write ( c ) ; in . close () ; out . close () ; } catch ( IOException e ) { System . err . println ( " Eroare la operatiile cu fisiere ! " ) ; e . printStackTrace () ; } } } In cazul ˆ care vom lansa aplicatia iar ˆ directorul curent nu exist˘ un ın ¸ ın a fi¸ier cu numele ”in.txt”, va fi generat˘ o exceptie de tipul s a ¸ FileNotFoundException. Aceasta va fi prins˘ de program deoarece, IOException a este superclas˘ pentru FileNotFoundException. a Dac˘ exist˘ fi¸ierul ”in.txt”, aplicatia va crea un nou fi¸ier ”out.txt” ˆ care a a s ¸ s ın va fi copiat continutul primului. Dac˘ exist˘ deja fi¸ierul ”out.txt” el va fi re- ¸ a a s
  • 106.
    4.2. FOLOSIREA FLUXURILOR 105 scris. Dac˘ doream s˘ facem operatia de ad˘ugare(append) ¸i nu de rescriere a a ¸ a s pentru fi¸ierul ”out.txt” foloseam: s FileWriter out = new FileWriter("out.txt", true); 4.2.5 Citirea ¸i scrierea cu buffer s Clasele 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 ın sul de citire/scriere a informatiilor, reducˆnd astfel numarul de acces˘ri ale ¸ a a dispozitivului 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 a folosirea 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˘a tampon. 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 ın flux ¸i, odat˘ cu citirea caracterului, vor fi memorati ˆ buffer ¸i caracterele s a ın s care ˆ urmeaz˘. Evident, BufferedInputStream functioneaz˘ dup˘ acela¸i ıi a ¸ a a s principiu, singura diferent˘ fiind faptul c˘ sunt cititi octeti. ¸a a ¸ ¸ Similar lucreaza ¸i clasele BufferedWriter ¸i BufferedOutputStream. s s La operatiile de scriere datele scrise nu vor ajunge direct la destinatie, ci vor ¸ ¸ fi memorate jntr-un buffer de o anumit˘ dimensiune. Atunci cˆnd bufferul a a este plin, continutul acestuia va fi transferat automat la destinatie. ¸ ¸ Fluxurile de citire/scriere cu buffer sunt fluxuri de procesare ¸i sunt s folosite prin suprapunere cu alte fluxuri, dintre care obligatoriu unul este primitiv. BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream("out.dat"), 1024) //1024 este dimensiunea bufferului Constructorii cei mai folositi ai acestor clase sunt 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 a aceasta 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 a acestea, clasele pentru scriere prin buffer mai au ¸i metoda flush care gole¸te s s explicit zona tampon, chiar dac˘ aceasta nu este plin˘. a a BufferedWriter out = new BufferedWriter( new FileWriter("out.dat"), 1024) //am creat un flux cu buffer de 1024 octeti for(int i=0; i<1000; i++) out.write(i); //bufferul nu este plin, in fisier nu s-a scris nimic out.flush(); //bufferul este golit, datele se scriu in fisier Metoda readLine Este specific˘ fluxurilor de citire cu buffer ¸i permite citirea linie cu linie a a s datelor de intrare. O linie reprezint˘ o succesiune de caractere terminat˘ cu a a simbolul pentru sfˆr¸it de linie, dependent de platforma de lucru. Acesta as este reprezentat ˆ Java prin secventa escape ’n’; ın ¸ BufferedReader br = new BufferedReader(new FileReader("in")) String linie; while ((linie = br.readLine()) != null) { ... //proceseaza linie } br.close(); }
  • 108.
    4.2. FOLOSIREA FLUXURILOR 107 4.2.6 Concatenarea fluxurilor Clasa SequenceInputStream permite unei aplicatii s˘ combine serial mai a multe fluxuri de intrare astfel ˆ at acestea s˘ apar˘ ca un singur flux de ıncˆ a a intrare. Citirea datelor dintr-un astfel de flux se face astfel: se cite¸te din s primul flux de intrare specificat pˆna cˆnd se ajunge la sfˆrsitul acestuia, a a a dup˘ care primul flux de intrare este ˆ a ınchis ¸i se deschide automat urm˘torul s a flux de intrare din care se vor citi ˆ continuare datele, dup˘ care procesul se ın a repet˘ 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 a tipul InputStream. Cel de-al doilea construie¸te un flux de intrare care combin˘ doar dou˘ fluxuri s a a s1 ¸i s2, primul flux citit fiind s1. s Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea a dou˘ 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 datelor Un flux de filtrare se ata¸eaz˘ altui flux pentru a filtra datele care sunt s a citite/scrise de c˘tre acel flux. Clasele pentru filtrarea datelor superclasele a abstracte: • FilterInputStream - pentru filtrarea fluxurilor de intrare ¸i s • FilterOutputStream - pentru filtrarea fluxurilor de ie¸ire. s Cele mai importante fluxruri pentru filtrarea datelor sunt implementate de 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 a interpretate sub alt˘ form˘. A¸a cum am v˘zut la citirea/scrierea cu zon˘ a a s a a tampon, clasele de filtrare BufferedInputStream ¸i BufferedOutputStream s colecteaz˘ datele unui flux ˆ a ıntr-un buffer, urmˆnd ca citirea/scrierea s˘ se a a fac˘ prin intermediu acelui buffer. a A¸adar, fluxurile de filtrare nu elimin˘ date citite sau scrise de un anumit s a flux, ci introduc o noua modalitate de manipulare a lor, ele mai fiind numite ¸i fluxuri de procesare. Din acest motiv, fluxurile de filtrare vor contine s ¸ anumite metode specializate pentru citirea/scrierea datelor, altele decˆt cele a comune tuturor fluxurilor. De exemplu, clasa BufferedInputStream pune la 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 s ocup˘ efectiv de citirea/scrierea datelor: a FluxFiltrare numeFlux = new FluxFiltrare(referintaAltFlux); 4.2.8 Clasele DataInputStream ¸i DataOutputStream s Aceste clase ofer˘ metode prin care un flux nu mai este v˘zut ca o ˆ a a ınsiruire de octeti, ci de date primitive. Prin urmare, vor furniza metode pentru ¸ citirea ¸i scrierea datelor la nivel de tip primitiv ¸i nu la nivel de octet. s s Clasele care ofer˘ un astfel de suport implementeaz˘ interfetele DataInput, a a ¸ respectiv DataOutput. Acestea definesc metodele pe care trebuie s˘ le pun˘ la a a dispozitie ˆ vederea citireii/scrierii datelor de tip primitiv. Cele mai folosite ¸ ın metode, altele decˆt cele comune tuturor fluxurilor, sunt date ˆ tabelul de a ın mai 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- s cate de interfetele DataInput ¸i DataOutput ¸i pot provoca exceptii de tipul s s ¸ IOException. Denumirile lor sunt sugestive pentru tipul de date pe care ˆ ıl prelucreaz˘. mai putin readUTF ¸i writeUTF care se ocup˘ cu obiecte de tip a ¸ s a String, fiind singurul tip referint˘ permis de aceste clase. ¸a Scrierea datelor folosind fluxuri de acest tip se face ˆ format binar, ceea ın ce ˆı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 s DataInputStream ¸i DataOutputStream permit serializarea tipurilor prim- s itive ¸i a ¸irurilor de caractere. Serializarea celorlalte tipuri referint˘ va fi s s ¸a f˘cut˘ prin intermediul altor clase, cum ar fi ObjectInputStream ¸i a a s ObjectOutputStream (vezi ”Serializarea obiectelor”). 4.3 Intr˘ri ¸i ie¸iri formatate a s s Incepˆnd cu versiunea 1.5, limbajul Java pune la dispozitii modalit˘¸i sim- a ¸ at plificate pentru afi¸area formatat˘ a unor informatii, respectiv pentru citirea s a ¸ de date formatate de la tastatur˘. a 4.3.1 Intr˘ri formatate a Clasa 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 a al constructorului fluxul System.in:
  • 112.
    4.4. FLUXURI STANDARDDE 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 s Clasele PrintStream ¸i PrintWriter pun la dispozitiile, pe lˆng˘ metodele s ¸ a a print, println care ofereau posibilitatea de a afi¸a un ¸ir de caractere, ¸i s s s metodele format, printf (echivalente) ce permit afi¸area formatat˘ a unor s a variabile. System.out.printf("%s %8.2f %2d %n", nume, salariu, varsta); Formatarea ¸irurilor de caractere se bazeaz˘ pe clasa java.util.Formatter. s a 4.4 Fluxuri standard de intrare ¸i ie¸ire s s Mergˆnd pe linia introdus˘ de sistemul de operare UNIX, orice program Java a a are : • 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 s descriu fluxuri de date care comunic˘ cu dispozitivele standard ale sistemului. a Aceste 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 s el 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 ¸ ¸ s de 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 aa este PrintStream, clas˘ concret˘ pentru scrierea datelor. In schimb, fluxul a a standard de intrare System.out este de tip InputStream, care este o clas˘ a abstract˘, deci pentru a-l putea utiliza eficient va trebui sa-l folosim ˆ a ımpreuna cu un flux de procesare(filtrare) care s˘ permit˘ citirea facil˘ a datelor. a a a 4.4.2 Citirea datelor de la tastatur˘ a Uzual, vom dori s˘ folosim metoda readLine pentru citirea datelor de la a tastatura ¸i din acest motiv vom folosi intrarea standard ˆ s ımpreun˘ cu o clas˘ a a de procesare care ofer˘ aceast˘ metod˘. Exemplul tipic este: a a a BufferedReader stdin = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Introduceti o linie:"); String linie = stdin.readLine() System.out.println(linie); In exemplul urm˘tor este prezentat un program care afi¸eaza liniile intro- a s duse de la tastatur˘ pˆn˘ ˆ momentul ˆ care se introduce linia ”exit” sau a a a ın ın o linie vid˘ ¸i mentioneaz˘dac˘ ¸irul respectiv reprezint˘ un num˘r sau nu. as ¸ a as a a
  • 114.
    4.4. FLUXURI STANDARDDE 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 a de la tastatur˘ este folosirea clasei java.util.Scanner. a 4.4.3 Redirectarea fluxurilor standard Redirectarea fluxurilor standard presupune stabilirea unei alte surse decˆt a tastatura pentru citirea datelor, respectiv alte destinatii decˆt ecranul pentru ¸ a cele dou˘ fluxuri de ie¸ire. In clasa System exist˘ urm˘toarele metode statice a s a a care 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 s date pe ecran. Putem redirecta afisarea c˘tre un fi¸ier pe care s˘-l citim dup˘ a s a a executia programului. Secventa clasic˘ de redirectare a ie¸irii este c˘tre un ¸ ¸ a s a fi¸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-o manier˘ 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 ın sol˘ care prime¸te mai multe valori de intrare. Pentru a nu le scrie de la a s tastatur˘ 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 a testarea programului a luat sfˆrsit redirectarea poate fi eliminat˘, datele fiind a a cerute din nou de la tastatur˘. a Listing 4.4: Exemplu de folosire a redirect˘rii: a import java . io .*; class Redirectare { public static void main ( String [] args ) { try { BufferedInputS t r ea m in = new B uf f e re d I np u tS t r ea m ( new FileInputStream ( " intrare . txt " ) ) ; PrintStream out = new PrintStream ( new FileOutputStream ( " rezultate . txt " ) ) ; PrintStream err = new PrintStream ( new FileOutputStream ( " erori . txt " ) ) ; System . setIn ( in ) ; System . setOut ( out ) ; System . setErr ( err ) ; BufferedReader br = new BufferedReader ( new InputStream Reader ( System . in ) ) ;
  • 116.
    4.4. FLUXURI STANDARDDE 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) a Clasa 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 a octeti sau caractere, se vor citi, pe rˆnd, atomii lexicali ai fluxului respectiv. ¸ a Printr-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 separator Atomii lexicali sunt desp˘rtiti ˆ a ¸ ıntre ei de separatori. Implicit, ace¸ti separa- s tori 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 s variabilelor: • 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˘ a tipul atomului lexical citit ¸i scrie ˆ variabilele nval sau sval valoarea core- s ın spunzato˘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 a pˆna se ajunge la sfˆrsitul fluxului (TT EOF). In cadrul buclei ”while” se deter- a a min˘ tipul atomul curent curent (ˆ a ıntors de metoda nextToken) ¸i apoi se afl˘ s a valoarea 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 a citim 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 ın String. 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 ¸ s Acestea 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 IOException RandomAccessFile(StringnumeFisier, StringmodAcces) throws IOException unde 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 s Exemple: RandomAccesFile f1 = new RandomAccessFile("fisier.txt", "r"); //deschide un fisier pentru citire RandomAccesFile f2 = new RandomAccessFile("fisier.txt", "rw"); //deschide un fisier pentru scriere si citire Clasa RandomAccesFile suport˘ notiunea de pointer de fi¸ier. Acesta este a ¸ s un indicator ce specific˘ pozitia curent˘ ˆ fi¸ier. La deschiderea unui fi¸ier a ¸ a ın s s pointerul are valoarea 0, indicˆnd ˆ a ınceputul fi¸ierului. Apeluri la metodele s de 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 s metode 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 119 4.6 Clasa File Clasa File nu se refer˘ doar la un fi¸ier ci poate reprezenta fie un fi¸ier a s s anume, fie multimea fi¸ierelor dintr-un director. s Specificarea unui fi¸ier/director se face prin specificarea c˘ii absolute spre s a acel fi¸ier sau a c˘ii relative fat˘ de directorul curent. Acestea trebuie s˘ re- s a ¸a a specte conventiile de specificare a c˘ilor ¸i numelor fi¸ierelor de pe platforma ¸ a s s de lucru. Utilitate clasei File const˘ ˆ furnizarea unei modalit˘¸i de a abstractiza a ın at dependentele cailor ¸i numelor fi¸ierelor fat˘de ma¸ina gazd˘, precum ¸i ¸ s s ¸a s a s punerea la dispozitie a unor metode pentru lucrul cu fisere ¸i directoare la ¸ s nivelul sistemului de operare. Astfel, ˆ aceast˘ clas˘ vom g˘si metode pentru testarea existentei, ¸tergerea, ın a a a ¸ s redenumirea unui fi¸ier sau director, crearea unui director, listarea fi¸ierelor s s dintr-un director, etc. Trebuie mentionat ¸i faptul c˘ majoritatea constructorilor fluxurilor care ¸ s a permit accesul la fi¸iere accept˘ ca argument un obiect de tip File ˆ locul s a ın unui ¸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 s prezentate prin intermediul exemplului urm˘tor care listeaz˘ fi¸ierele ¸i sub- a a s s directoarele unui director specificat ¸i, pentru fiecare din ele afi¸eaz˘ diverse s s a informatii: ¸ Listing 4.6: Listarea continutului unui director ¸ /* Programul listeaza fisierele si subdirectoarele unui director . Pentru fiecare din ele vor fi afisate diverse informatii . Numele directorului este primit ca argument de la linia de comanda , sau este directorul curent . */ import java . io .*;
  • 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 5 Interfete ¸ 5.1 Introducere 5.1.1 Ce este o interfat˘ ? ¸a Interfetele duc conceptul de clas˘ abstract˘ cu un pas ˆ ¸ a a ınainte prin eliminarea oric˘ror implement˘ri de metode, punˆnd ˆ practic˘ unul din conceptele a a a ın a program˘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 ¸a protocol de comunicare ˆıntre obiecte. O interfat˘ Java define¸te un set de metode dar nu specific˘ nici o imple- ¸a s a mentare pentru ele. O clas˘ care implementeaz˘ o interfat˘ trebuie obligato- a a ¸a riu s˘ specifice implement˘ri pentru toate metodele interfetei, supunˆndu-se a a ¸ a a¸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 ¸a public˘ este accesibil˘ tuturor claselor, indiferent de pachetul din care fac a a parte, implicit nivelul de acces fiind doar la nivelul pachetului din care face parte interfata. ¸ O interfat˘ poate extinde oricˆte interfete. Acestea se numesc superinterfete ¸a a ¸ ¸ ¸i sunt separate prin virgul˘. (vezi ”Mo¸tenirea multipl˘ prin intermediul s a s a interfetelor”). ¸ 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. a 5.2.2 Implementarea unei interfete ¸ Implementarea uneia sau mai multor interfete de c˘tre o clas˘ se face prin ¸ a a intermediul 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 ¸ a nici una. In cazul ˆ care o clas˘ implementeaz˘ o anumit˘ interfat˘, atunci tre- ın a a a ¸a buie 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 ¸a mai trebuie modificat˘, ˆ sensul c˘ ad˘ugarea unor metode noi sau schim- a ın a a barea signaturii metodelor existente vor duce la erori ˆ compilarea claselor ın care o implementeaz˘. Evident, o clas˘ poate avea ¸i alte metode ¸i variabile a a s s membre ˆ afar˘ de cele definite ˆ interfat˘. ın a ın ¸a Atentie ¸ Modificarea unei interfete implic˘ modificarea tuturor claselor care im- ¸ a plementeaz˘ acea interfat˘. a ¸a O interfat˘ nu este o clas˘, dar orice referint˘ de tip interfat˘ poate primi ¸a a ¸a ¸a ca valoare o referinta la un obiect al unei clase ce implementeaz˘ interfata ¸ a ¸ respectiv˘. Din acest motiv, interfetele pot fi privite ca tipuri de date ¸i vom a ¸ s spune adesea c˘ un obiect are tipul X, unde X este o interfat˘, dac˘ acesta a ¸a a este o instant˘ a unei clase ce implementeaz˘ interfata X. ¸a a ¸ Implementarea unei interfete poate s˘ fie ¸i o clas˘ abstract˘. ¸ a s a a 5.2.3 Exemplu: implementarea unei stive S˘ consider˘m urm˘torul exemplu. Dorim s˘ implement˘m un nou tip de a a a a a date numit Stack, care s˘ modeleze notiunea de stiv˘ de obiecte. Obiectele a ¸ a de tip stiv˘, indiferent de implementarea lor, vor trebui s˘ contin˘ metodele: a a ¸ a • push - adaug˘ un nou element in 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 a folosind un vector sau o list˘ ˆ antuit˘, ambele solutii avˆnd avantaje ¸i a ınl˘ ¸ a ¸ a s dezavantaje. Prima solutie este mai simplu de ˆ ¸eles, ˆ timp ce a doua este ¸ ınt ın mai eficient˘ din punctul de vedere al folosirii memoriei. Deoarece nu dorim a s˘ leg˘m tipul de date Stack de o anumit˘ implementare structural˘, ˆ vom a a a a ıl defini 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˘ma s˘ 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˘ ¸ a public 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 ¸a modificatorul public, ele sunt totu¸i publice ¸i trebuie declarate ca atare ˆ s s ın clas˘. a Trebuie remarcat ¸i faptul c˘ metoda toString este definit˘ deja ˆ clasa s a a ın Object, 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 a este de fapt supradefinirea ei.
  • 128.
    5.2. FOLOSIREA INTERFETELOR ¸ 127 O alt˘ observatie important˘ se refer˘ la faptul c˘ trebuie s˘ declar˘m ˆ a ¸ a a a a a ın cadrul interfetei ¸i exceptiile aruncate de metode, ce trebuie obligatoriu ¸ s ¸ tratate. S˘ vedem acum modalitatea de implementare a stivei folosind o list˘ a a ˆ antuit˘: ınl˘ ¸ a Listing 5.4: Implementarea stivei folosind o list˘ a // Implementarea stivei folosind o lista inlantuita . public class StackImpl2 implements Stack { class Node { // Clasa interna ce reprezinta un nod al listei Object item ; // informatia din nod Node link ; // legatura la urmatorul nod Node ( Object item , Node link ) { this . item = item ; this . link = link ; } } private Node top = null ; // Referinta la varful stivei public void push ( Object item ) { Node node = new Node ( item , top ) ; top = node ; } public void pop () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; top = top . link ; } public Object peek () throws StackException { if ( empty () ) throw new StackException ( " Stiva este vida ! " ) ; return top . item ; } public boolean empty () { return ( top == null ) ; } public String toString () { String s = " " ; Node node = top ; while ( node != null ) {
  • 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 s interfat˘ 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 a nu 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 s tipul de date nou creat ¸i cele dou˘ implement˘ri ale sale: s a a Listing 5.5: Folosirea stivei public class TestStiva { public static void afiseaza ( Stack s ) { System . out . println ( " Continutul stivei este : " + s ) ; } public static void main ( String args []) { try { Stack s1 = new StackImpl1 () ; s1 . push ( " a " ) ; s1 . push ( " b " ) ; afiseaza ( s1 ) ; Stack s2 = new StackImpl2 () ; s2 . push ( new Integer (1) ) ; s2 . push ( new Double (3.14) ) ; afiseaza ( s2 ) ; } catch ( StackException e ) { System . err . println ( " Eroare la lucrul cu stiva ! " ) ; e . printStackTrace () ; } } } Observati folosirea interfetei Stack ca un tip de date, ce aduce flexibilitate ¸ ¸ sporit˘ ˆ manevrarea claselor ce implementeaz˘ tipul respectiv. Metoda a ın a
  • 130.
    5.3. INTERFETE SICLASE ABSTRACTE ¸ ¸ 129 afiseaza accept˘ ca argument orice obiect al unei clase ce implementeaz˘ a a Stack. Observatie¸ In pachetul java.util exist˘ clasa Stack care modeleaz˘ notiune de stiv˘ a a ¸ a de obiecte ¸i, evident, aceasta va 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 as pur demonstrativ. 5.3 Interfete ¸i clase abstracte ¸ s La prima vedere o interfat˘ nu este altceva decˆt o clas˘ abstract˘ ˆ care ¸a a a a ın toate metodele sunt abstracte (nu au nici o implementare). A¸adar, o clas˘ abstract˘ nu ar putea ˆ s a a ınlocui o interfat˘ ? ¸a Raspunsul la intrebare depinde de situatie, ˆ a ˆ general este ’Nu’. ¸ ıns˘ ın Deosebirea const˘ ˆ faptul c˘ unele clase sunt fortate s˘ extind˘ o anumit˘ a ın a ¸ a a a clas˘ (de exemplu orice applet trebuie s˘ fie subclasa a clasei Applet) ¸i nu ar a a s mai putea sa extind˘ o alt˘ clas˘, deoarece ˆ Java nu exista decˆt mo¸tenire a a a ın a s simpla. Fara folosirea interfetelor nu am putea forta clasa respectiv˘ s˘ ¸ ¸ a a respecte diverse tipuri de protocoale. La nivel conceptual, diferenta const˘ ˆ ¸ a ın: • extinderea unei clase abstracte forteaz˘ o relatie ˆ ¸ a ¸ ıntre clase; • implementarea unei interfete specific˘ doar necesitatea implement˘rii ¸ a a unor anumie metode. In multe situatii interfetele ¸i clasele abstracte sunt folosite ˆ ¸ ¸ s ımpreun˘a pentru a implementa cˆt mai flexibil ¸i eficient o anumit˘ ierarhie de clase. Un a s a exemplu sugestiv este dat de clasele ce descriu colectii. Ca sa particulariz˘m, ¸ a exist˘: a • interfata List care impune protocolul pe care trebuie s˘ ˆ respecte o ¸ a ıl clas˘ de tip list˘, a a • clasa abstract˘ AbstractList care implementeaz˘ interfata List ¸i a a ¸ s ofer˘ 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 a interfete sau ca o interfat˘ s˘ extind˘ mai multe interfete (s˘ aib˘ mai multe ¸ ¸a a a ¸ a a superinterfete) ¸ class NumeClasa implements Interfata1, Interfata2, ... interface NumeInterfata extends Interfata1, Interfata2, ... O interfat˘ mosteneste 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 ¸ s responsabilitatea 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 a sau 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 stil prost de programare. In cazul in care acest lucru se ˆ ampl˘, compilatorul ıntˆ a nu va furniza eroare decˆt dac˘ se ˆ a a ıncearc˘ referirea constantelor ambigue a f˘r˘ a le prefixa cu numele interfetei sau dac˘ metodele cu acela¸i nume nu aa ¸ a s pot fi deosbite, cum ar fi situatia cˆnd au aceea¸i list˘ de argumente dar ¸ a s a tipuri 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˘.a 5.5 Utilitatea interfetelor ¸ Dup˘ cum am v˘zut, o interfat˘ define¸te un protocol ce poate fi implementat a a ¸a s de 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 constante Deoarece orice variabil˘ a unei interfete este implicit declarat˘ cu public, a ¸ a static si final, interfetele reprezint˘ o metod˘ convenabil˘ de creare a unor ¸ a a a grupuri de constante care s˘ 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 genul NumeInterfata.constanta, ca ˆ exemplul de mai jos: ın if (luna < Luni.DEC) luna ++ else luna = Luni.IAN; 5.5.2 Transmiterea metodelor ca parametri Deoarece nu exist˘ pointeri propriu-zi¸i, transmiterea metodelor ca parametri a s este realizat˘ ˆ Java prin intermediul interfetelor. Atunci cˆnd o metod˘ a ın ¸ a a trebuie s˘ primeasc˘ ca argument de intrare o referint˘ la o alt˘ functie a a ¸a a ¸ necesar˘ executiei sale, cunoscut˘ doar la momentul executiei, atunci 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 a s a ınt a a a clarifica lucrurile. Primul exemplu se refer˘ la explorarea nodurilor unui graf. In fiecare a nod 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. ¸˘ a interface 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 functii class AfisareRo implements Functie { public void executa(Nod v) { System.out.println("Nodul curent este: " + v); } } class AfisareEn implements Functie { public void executa(Nod v) { System.out.println("Current node is: " + v); } } public class TestCallBack { public static void main(String args[]) { Graf G = new Graf(); G.explorare(new AfisareRo()); G.explorare(new AfisareEn()); } } Al doilea xemplu va fi prezentat ˆ sectiunea urm˘toare, ˆ ın ¸ a ıntrucˆt face a parte din API-ul standard Java ¸i vor fi puse ˆ evident˘, prin intermediul s ın ¸a s˘u, ¸i alte tehnici de programare. a s 5.6 Interfata FilenameFilter ¸ Instantele claselor ce implementeaz˘ aceasta interfat˘ sunt folosite pentru a ¸ a ¸a crea filtre pentru fi¸iere ¸i sunt primite ca argumente de metode care listeaz˘ s s a continutul 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 a criteriul de filtrare ¸i anume, testeaz˘ dac˘ numele fi¸ierului primit ca para- s a a s metru ˆ ı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 a primeasca criteriul de filtrare. In general, o clas˘ de specificare a unui filtru a are urm˘torul format: a class FiltruFisiere implements FilenameFilter { String filtru; // Constructorul FiltruFisiere(String filtru) { this.filtru = filtru; } // Implementarea metodei accept public boolean accept(File dir, String nume) { if (filtrul este indeplinit) return true; else return false; } } Metodele cele mai uzuale ale clasei String folosite pentru filtrarea fi¸ierelor s sunt: • 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 ¸ a clasa File: String[] list (FilenameFilter filtru) Observati c˘ aici interfata este folosit˘ ca un tip de date, ea fiind substi- a ¸ a tuit˘ cu orice clas˘ care o implementeaz˘. Acesta este un exemplu tipic de a a a transmitere 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 s rectorul 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 anonime In cazul ˆ care nu avem nevoie de clasa care reprezint˘ filtrul pentru listarea ın a fi¸ierelor dintr-un director decˆt o singur˘ dat˘, pentru a evita crearea unei s a a a noi 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 obiectelor Am v˘zut ˆ primul capitol c˘ o solutie facil˘ ¸i eficient˘ de sortare a unui a ın a ¸ as a vector este folosirea metodei sort din clasa java.util.Arrays. int v[]={3, 1, 4, 2}; java.util.Arrays.sort(v); // Sorteaza vectorul v // Acesta va deveni {1, 2, 3, 4} In cazul ˆ care elementele din vector sunt de tip primitiv, ca in exem- ın plul de mai sus, nu exist˘ nici o problem˘ ˆ a determina ordinea fireasc˘ a a ın a a 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 ın s˘ 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) aa class Persoana { int cod ; String nume ; public Persoana ( int cod , String nume ) { this . cod = cod ; this . nume = nume ; } public String toString () { return cod + " t " + nume ; } }
  • 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˘ ¸a class Sortare { public static void main ( String args []) { Persoana p [] = new Persoana [4]; p [0] = new Persoana (3 , " Ionescu " ) ; p [1] = new Persoana (1 , " Vasilescu " ) ; p [2] = new Persoana (2 , " Georgescu " ) ; p [3] = new Persoana (4 , " Popescu " ) ; java . util . Arrays . sort ( p ) ; System . out . println ( " Persoanele ordonate dupa cod : " ) ; for ( int i =0; i < p . length ; i ++) System . out . println ( p [ i ]) ; } } 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 a trebui, ˆ ıntr-un fel sau altul, s˘ specific˘m acest lucru. a a 5.7.1 Interfata Comparable ¸ Interfata Comparable impune o ordine total˘ asupra obiectelor unei clase ce ¸ a o implementeaz˘. Aceast˘ ordine se nume¸te ordinea natural˘ a clasei ¸i este a a s a s specificat˘ 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 ¸ a metoda compareTo care trebuie s˘ returneze: a • o valoare strict negativ˘: dac˘ obiectul curent (this) este mai mic a a decˆ obiectul primit ca argument; a • zero: dac˘ obiectul curent este egal decˆ obiectul primit ca argument; a a
  • 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 a determin˘ dac˘ dou˘ obiecte sunt egale (au aceea¸i valoare). Spunem c˘ a a a s a ordinea natural˘ a unei clase C este consitent˘ cu equals dac˘ ¸i numai a a a s dac˘ (e1.compareTo((Object)e2) == 0) are aceeas¸i valoare logic˘ cu a s a e1.equals((Object)e2, pentru orice e1, e2 instante ale lui C. ¸ null nu este instant˘ a nici unei clase ¸i e.compareTo(null) trebuie s˘ ¸a s a arunce o exceptie de tip NullPointerException chiar dac˘ e.equals(null) ¸ a returneaz˘ false. a S˘ presupunem c˘ dorim ca ordinea natural˘ a persoanelor s˘ fie dup˘ a a a a a codul lor intern. Listing 5.10: Clasa Persoana cu suport pentru comparare class Persoana implements Comparable { int cod ; String nume ; public Persoana ( int cod , String nume ) { this . cod = cod ; this . nume = nume ; } public String toString () { return cod + " t " + nume ; } public boolean equals ( Object o ) { if (!( o instanceof Persoana ) ) return false ; Persoana p = ( Persoana ) o ; return ( cod == p . cod ) && ( nume . equals ( p . nume ) ) ; } public int compareTo ( Object o ) { if ( o == null ) throw new Null P o i n t e r E x c e p t i o n () ; if (!( o instanceof Persoana ) ) throw new Clas sCas tExce ptio n ( " Nu pot compara ! " ) ; Persoana p = ( Persoana ) o ;
  • 142.
    5.7. COMPARAREA OBIECTELOR 141 return ( cod - p . cod ) ; } } Observati folosirea operatorului instanceof, care verific˘ dac˘ un obiect ¸ a a este instant˘ a unei anumite clase. Metoda compareTo va arunca o exceptie ¸a ¸ de tipul ClassCastException dac˘ se ˆ a ıncearc˘ compararea unui obiect de a tip Persoana cu un obiect de alt tip. Metoda equals va returna, pur ¸i s simplu, false. 5.7.2 Interfata Comparator ¸ In cazul ˆ care dorim s˘ 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 a solutie. Aceasta este oferit˘ tot de metoda sort din clasa java.util.Arrays, ¸ a dar ˆ varianta ˆ care, pe lˆng˘ vectorul ce trebuie sortat, vom transmite ın ın a a un argument de tip Comparator care s˘ specifice modalitatea de comparare a a elementelor. Interfata java.util.Comparator contine metoda compare, care impune ¸ ¸ o ordine total˘ asupra elementelor unei colectii. Aceasta returneaz˘ un ˆ a ¸ a ıntreg cu aceea¸i semnificatie ca la metoda compareTo a interfetei Comparator ¸i s ¸ ¸ s are 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 a Pentru definirea comparatorului vom folosi o clas˘ anonim˘. a a Listing 5.11: Sortarea unui vector folosind un comparator import java . util .*; class Sortare { public static void main ( String args []) { Persoana p [] = new Persoana [4]; p [0] = new Persoana (3 , " Ionescu " ) ; p [1] = new Persoana (1 , " Vasilescu " ) ; p [2] = new Persoana (2 , " Georgescu " ) ; p [3] = new Persoana (4 , " Popescu " ) ; Arrays . sort (p , new Comparator () { public int compare ( Object o1 , Object o2 ) { Persoana p1 = ( Persoana ) o1 ; Persoana p2 = ( Persoana ) o2 ; return ( p1 . nume . compareTo ( p2 . nume ) ) ;
  • 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 ¸ as compareTo, clasa String implemenˆnd interfata Comparable. a ¸ 5.8 Adaptori In cazul ˆ care o interfat˘ contine mai multe metode ¸i, la un moment ın ¸a ¸ s dat, avem nevoie de un obiect care implementeaz˘ interfata respectiv dar nu a ¸ specific˘ cod decˆt pentru o singur˘ metod˘, el trebui totu¸i s˘ implementeze a a a a s a toate 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 a ale unor clase ce implementeaz˘ interfata X. Solutia la aceast˘ problem˘ este a ¸ ¸ a a folosirea adaptorilor. Definitie ¸ Un adaptor este o clas˘ abstract˘ care implementeaz˘ o anumit˘ interfat˘ a a a a ¸a f˘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 ¸ a abstract˘, 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 6 Organizarea claselor 6.1 Pachete Definitie ¸ Un pachet este o colectie de clase ¸i interfete ˆ ¸ s ¸ ınrudite din punctul de vedere al functionalit˘¸ii lor. Sunt folosite pentru g˘sirea ¸i utilizarea mai ¸ at a s u¸oar˘ a claselor, pentru a evita conflictele de nume ¸i pentru a controla s a s accesul la anumite clase. In alte limbaje de programare pachetele se mai numesc libr˘rii sau bibilioteci. a 6.1.1 Pachetele standard (J2SDK) Platforma standard de lucru Java se bazeaz˘ pe o serie de pachete cu ajutorul a c˘rora se pot construi ˆ a ıntr-o manier˘ simplificat˘ aplicatiile. Exist˘ deci un a a ¸ a set de clase deja implementate care modeleaz˘ structuri de date, algoritmi sau a diverse notiuni esentiale ˆ dezvoltarea unui program. Cele mai importante ¸ ¸ ın pachete ¸i suportul oferit de lor sunt: s • java.lang - clasele de baz˘ ale limbajului Java a • java.io - 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 pachet Conform specificatiilor de acces ale unei clase ¸i ale mebrilor ei, doar clasele ¸ s publice ¸i membrii declarati publici ai unei clase sunt accesibili ˆ afara pa- s ¸ ın chetului ˆ 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 ın pachet. Pentru a folosi o clas˘ public˘ dintr-un anumit pachet, sau pentru a apela a a o metod˘ public˘ a unei clase publice a unui pachet, exist˘ trei solutii: a a a ¸ • 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 scurt al 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 ın acelei 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 a s˘ 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 declararea vreunei clase sau interfete. ¸ 6.1.3 Importul unei clase sau interfete ¸ Se face prin instructiunea import ˆ care specific˘m numele complet al clasei ¸ ın a sau interfetei pe care dorim s˘ o folosim dintr-un anumit pacehet: ¸ a import numePachet.numeClasa; //Pentru exemplul nostru: import java.awt.Button; import java.awt.TextField; Din acest moment, vom putea folosi ˆ clasele fi¸ierului ˆ care am plasat ın s ın instructiunea 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 ın nevoie decˆt de cˆteva clase din pachetul respectiv. Dac˘ ˆ exemplul nostru a a a ın am avea nevoie ¸i de clasele Line, Point, Rectangle, Polygon, ar trebui s˘ s a avem 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 ıntregul pachet ¸i nu al fiec˘rei clase ˆ parte. s a ın 6.1.4 Importul la cerere dintr-un pachet Importul la cerere dintr-un anumit pachet se face printr-o instructiune import ¸ ˆ care specific˘m numele pachetului ale c˘rui clase ¸i interfete dorim s˘ le ın a a s ¸ a folosim, 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 ın instructiunea de import numele scurt al tuturor claselor pachetului importat: ¸ Button b = new Button("OK"); Point p = new Point(0, 0); Atentie ¸ * nu are semnificatia uzual˘ de la fi¸iere de wildcard (masc˘) ¸i nu poate fi ¸ a s a s folosit decˆt ca atare. O expresie de genul import java.awt.C*; va produce a o 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 a folosind numele complet, ˆ caz contrar fiind semnalat˘ o ambiguitate de ın a c˘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 a pachete: • pachetul java.lang import java.lang.*; // Poate sau nu sa apara // Mai bine nu... • pachetul curent • pachetul implicit (f˘r˘ nume) aa 6.1.5 Importul static Aceast˘ facilitate, introdus˘ ˆ a a ıncepˆnd cu versiunea 1.5, permite referirea con- a stantelor statice ale unei clase f˘r˘ a mai specifica numele complet al aces- aa teia ¸i este implementat˘ prin ad˘ugarea cuvˆntului cheie static dup˘ cel de s a a a a import: import static numePachet.NumeClasa.*; Astfel, ˆ loc s˘ ne referim la constantele clasei cu expresii de tipul ın a NumeClasa.CONSTANTA, putem folosi doar numele constantei.
  • 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 s clasa ˆ sine. ın 6.1.6 Crearea unui pachet Toate clasele ¸i interfetele Java apartin la diverse pachete, grupate dup˘ s ¸ a functionalitatea lor. Dup˘ cum am v˘zut clasele de baz˘ se g˘sesc ˆ pa- ¸ a a a a ın chetul java.lang, clasele pentru intr˘ri/ie¸iri sunt ˆ java.io, clasele pentru a s ın interfata grafic˘ ˆ java.awt, etc. ¸ a ın Crearea unui pachet se realizeaz˘ prin scriere la ˆ a ınceputul fi¸ierelor surs˘ s a ce contin clasele ¸i interfetele pe care dorim s˘ le grup˘m ˆ ¸ s ¸ a a ıntr-un pachet a instructiunii: 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 din acela¸i pachet grafuri. s Instructiunea package actioneaz˘ asupra ˆ ¸ ¸ a ıntregului fi¸ier surs˘ la ˆ s a ınceputul c˘ruia apare. Cu alte cuvinte nu putem specifica faptul c˘ anumite clase a a dintr-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 a parte din pachetul implicit (care nu are nici un nume). In general, pachetul implicit este format din toate clasele ¸i intefetele directorului curent de lucru. s ¸ Este recomandat ˆ a ca toate clasele ¸i intefetele s˘ fie plasate ˆ pachete, ıns˘ s a ın pachetul implicit fiind folosit doar pentru aplicatii mici sau prototipuri. ¸ 6.1.7 Denumirea unui pachet Exist˘ posibilitatea ca doi programatori care lucreaz˘ la un proiect comun a a s˘ foloseasc˘ acela¸i nume pentru unele din clasele lor. De asemenea, se a a s poate ca una din clasele unei aplicatii s˘ aib˘ acela¸i nume cu o clas˘ a ¸ a a s a mediului Java. Acest lucru este posibil atˆt timp cˆt clasele cu acela¸i nume a a s se 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 a comun 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 lor Internet ˆ denumirea pachetelor implementate ˆ cadrul companiei, cum ın ın ar fi ro.companie.numePachet. In cadrul aceleiasi companii, conflictele de nume 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 s De exemplu, programatorul cu numele Ion al companiei XSoft, avˆnd contul a ion@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 ın vor fi integrate.
  • 153.
    152 CAPITOLUL 6. ORGANIZAREA CLASELOR 6.2 Organizarea fi¸ierelor s 6.2.1 Organizarea fi¸ierelor surs˘ s a Orice aplicatie nebanal˘ trebuie s˘ fie construit˘ folosind o organizare ier- ¸ a a a arhic˘ a componentelor sale. Este recomandat ca strategia de organizare a a fi¸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 a printr-un exemplu concret. S˘ presupunem c˘ dorim crearea unor compo- a a nente care s˘ reprezinte diverse notiuni matematice din domenii diferite, a ¸ cum ar fi geometrie, algebr˘, analiz˘, etc. Pentru a simplifica lucrurile, s˘ a a a presupunem c˘ dorim s˘ cre˘m clase care s˘ descrie urm˘toarele notiuni: a a a a a poligon, 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 s Ierarhia 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 ın mite corespunzator cu numele directoarelor ˆ care se gasesc: ın // Poligon.java package geometrie.plan; public class Poligon { . . . } // Cerc.java package geometrie.plan; public class Cerc { . . . } // Poliedru.java package geometrie.spatiu; public class Poliedru { . . . }
  • 155.
    154 CAPITOLUL 6. ORGANIZAREA CLASELOR // Sfera.java package geometrie.spatiu; public class Sfera { . . . } // Grup.java package algebra; public class Grup { . . . } // Functie.java package analiza; public class Functie { . . . } Matematica.java este clasa principal˘ a aplicatiei. a ¸ Dup˘ cum se observ˘, numele lung al unei clase trebuie s˘ descrie calea spre a a a acea clas˘ ˆ cadrul fi¸ierelor surs˘, relativ la directorul ˆ care se g˘se¸te a ın s a ın a s aplicatia. ¸ 6.2.2 Organizarea unit˘¸ilor de compilare (.class) at In urma compil˘rii fi¸ierelor surs˘ vor fi generate unit˘¸i de compilare pentru a s a at fiecare 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 a gaseasca ˆ ıntr-o ierarhie de directoare care s˘ reflecte numele pachetului din a care face parte clasa respectiv˘.a Implicit, ˆ urma compil˘rii fi¸ierele surs˘ ¸i unit˘¸ile de compilare se ın a s a s at g˘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.class Crearea acestei structuri ierarhice este facut˘ automat de c˘tre compilator. a a In directorul aplicatiei (matematica) cre˘m subdirectorul clase ¸i d˘m co- a s a manda: javac -sourcepath surse surse/Matematica.java -d clase sau javac -classpath surse surse/Matematica.java -d clase Optiunea -d specific˘ directorul r˘d˘cin˘ al ierarhiei de clase. In lipsa ¸ a a a a lui, fiecare unitate de compilare va fi plasat˘ ˆ acela¸i director cu fi¸ierul s˘u a ın s s a surs˘. a Deoarece compil˘m clasa principal˘ a plicatiei, vor fi compilate ˆ cascad˘ a a ¸ ın a toate clasele referite de aceasta, dar numai acestea. In cazul ˆ care dorim s˘ ın a compil˘m explicit toate fi¸ierele java dintr-un anumit director, de exemplu a s surse/geometrie/plan, putem folosi expresia: javac surse/geometrie/plan/*.java -d clase 6.2.3 Necesitatea organiz˘rii fi¸ierelor a s Organizarea fi¸ierelor surs˘ este necesar˘ deoarece ˆ momentul cˆnd compi- s a a ın a latorul ˆ alneste un nume de clas˘ el trebuie s˘ poat˘ identifica acea clas˘, ıntˆ a a a a ceea 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- at itatea interpretorului s˘ gaseasc˘ ¸i s˘ ˆ a a s a ıncarce ˆ memorie o anumit˘ clas˘ ˆ ın a a ın timpul executiei programului. ¸ Ins˘ aceast˘ organizare nu este suficient˘ deoarece specific˘ numai partea a a a a final˘ 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 a la 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 a fi¸ierele necesare unei aplicatii. Fiecare director din calea de cautare este s ¸ directorul imediat superior structurii de directoare corespunz˘toare numelor a pachetelor ˆ 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 a Setarea 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 JAR Fi¸ierele JAR (Java Archive) sunt arhive ˆ format ZIP folosite pentru s ın ˆ ımpachetarea aplicatiilor Java. Ele pot fi folosite ¸i pentru comprim˘ri ¸ s a obi¸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 jar Arhivatorul jar se g˘se¸te ˆ subdirectorul bin al directorului ˆ care este a s ın ın instalat kitul J2SDK. Mai jos sunt prezentate pe scurt operatiile uzuale: ¸ • Crearea unei arhive jar cf arhiva.jar 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 159 6.3.2 Executarea aplicatiilor arhivate ¸ Pentru a rula o aplicatie ˆ ¸ ımpachetat˘ ˆ a ıntr-o arhiv˘ JAR trebuie s˘ facem a a cunoscut˘ interpretorului numele clasei principale a aplicatiei. S˘ consider˘m a ¸ a a urm˘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 a lansa comanda: jar cvf mate.jar geometrie analiza algebra Matematica.class In urma acestei comenzi vom obtine arhiva mate.jar. Dac˘ vom ˆ a ıncerca s˘ lans˘m ˆ executie aceast˘ arhiv˘ prin comanda java -jar mate.jar a a ın ¸ a a vom obtine urm˘toarea eroare: ”Failed to load Main-Class manifest from ¸ a mate.jar”. Aceasta ˆ ınseamna c˘ ˆ fiserul Manifest.mf ce se gase¸te ˆ di- a ın s ın rectorul 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 a lansat˘ ˆ executie aplicatia ˆ a ın ¸ ¸ ımpachetat˘ ˆ acea arhiv˘ (dac˘ exist˘ o clas˘ a ın a a a a principal˘). a
  • 161.
    160 CAPITOLUL 6. ORGANIZAREA CLASELOR
  • 162.
    Capitolul 7 Colectii ¸ 7.1 Introducere O colectie este un obiect care grupeaz˘ mai multe elemente ˆ ¸ a ıntr-o singur˘a unitate. Prin intermediul colectiilor vom avea acces la diferite tipuri de ¸ date cum ar fi vectori, liste ˆ antuite, stive, multimi matematice, tabele de ınl˘ ¸ ¸ dispersie, etc. Colectiile sunt folosite atˆt pentru memorarea ¸i manipularea ¸ a s datelor, 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˘ a c˘ 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˘ a unitar˘, 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 ın unei 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 a problemei ˆ sine ¸i nu asupra modului de reprezentare ¸i manipulare a ın s s informatiilor. ¸ 7.2 Interfete ce descriu colectii ¸ ¸ Interfetele reprezint˘ nucleul mecanismului de lucru cu colectii, scopul lor ¸ a ¸ fiind de a permite utilizarea structurilor de date independent de modul lor de implementare. Collection modeleaz˘ o colectie la nivelul cel mai general, descriind un a ¸ grup de obiecte numite ¸i elementele sale. Unele implement˘ri ale acestei s a interfete permit existenta elementelor duplicate, alte implement˘ri nu. Un- ¸ ¸ a ele au elementele ordonate, altele nu. Platforma Java nu ofer˘ nici o im- a plementare direct˘ a acestei interfete, ci exist˘ doar implement˘ri ale unor a ¸ a a subinterfete 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 CEDESCRIU 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 s f˘r˘ a avea alte metode specifice. aa Dou˘ dintre clasele standard care ofer˘ implement˘ri concrete ale acestei a a a interfete 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 a Ordonarea elementelor se face conform ordinii lor naturale, sau conform cu ordinea dat˘ de un comparator specificat la crearea colectiei ¸i este mentinut˘ a ¸ s ¸ a automat la orice operatie efectuat˘ asupra multimii. Singura condittie este ¸ a ¸ ¸ ca, pentru orice dou˘ obiecte o1, o2 ale colectiei, apelul o1.compareT o(o2) a ¸ (sau comparator.compare(o1, o2), dac˘ este folosit un comparator) trebuie a s˘ fie valid ¸i s˘ nu provoace exceptii. a s a ¸ Fiind subclas˘ a interfetei Set, mo¸tene¸te metodele acesteia, oferind a ¸ s s metode suplimentare ce ¸in cont de faptul c˘ multimea este sortat˘: t a ¸ a public interface SortedSet extends Set { // Subliste SortedSet subSet(Object fromElement, Object toElement); SortedSet headSet(Object toElement);
  • 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 ¸a interfata Collection, avem metode pentru acces pozitional, c˘utare ¸i it- ¸ ¸ a s erare 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 CEDESCRIU COLECTII ¸ ¸ 165 Clase standard care implementeaz˘ aceast˘ interfat˘ sunt: ArrayList, a a ¸a LinkedList, Vector. Map descrie structuri de date ce asociaz˘ fiecarui element o cheie unic˘, a a dup˘ 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 a Definittai interfetei este prezentat˘ mai jos: ¸ ¸ a public interface Map { // Metode cu caracter general int size(); boolean isEmpty(); void clear(); // Operatii la nivel de element Object put(Object key, Object value); Object get(Object key); Object remove(Object key); boolean containsKey(Object key); boolean containsValue(Object value); // Operatii la nivel de multime void putAll(Map t); // Vizualizari ale colectiei public Set keySet(); public Collection values(); public Set entrySet(); // Interfata pentru manipularea unei inregistrari public interface Entry { Object getKey(); Object getValue(); Object setValue(Object value); } }
  • 167.
    166 CAPITOLUL 7. COLECTII ¸ Clase care implementeaz˘ interfat˘ Map sunt HashMap, TreeMap ¸i a ¸a s Hashtable. SortedMap este asem˘n˘toare cu interfata Map, la care se adaug˘ fap- a a ¸ a tul c˘ multimea cheilor dintr-o astfel de colectie este mentinut˘ ordonat˘ a ¸ ¸ ¸ a a ascendent conform ordinii naturale, sau conform cu ordinea dat˘ de un com- a parator specificat la crearea colectiei. Este subclasa a interfetei Map, oferind ¸ ¸ metode suplimentare pentru: extragere de subtabele, aflarea primei/ultimei chei, 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 ¸a 7.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 s au fost adaptate ˆ a¸a fel ˆ at s˘ se integreze ˆ noua abordare. Pe lˆng˘ ın s ıncˆ a ın a a acestea 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 ALECOLECTIILOR ¸ 167 Clasele de baz˘ care implementeaz˘ interfete ce descriu colectii au numele a a ¸ ¸ de forma < Implementare >< Interf ata >, unde ’implementare’ se refer˘ la a structura intern˘ folosit˘ pentru reprezentarea multimii, ¸i sunt prezentate a a ¸ s ˆ tabelul de mai jos, ˆ ın ımpreun˘ cu interfetele corespunz˘toare (clasele din a ¸ a vechiul 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 ın de mai jos: AbstractCollection - AbstractSet, AbstractList - HashSet, TreeSet... Vector-Stack AbstractMap - HashMap, TreeMap, HashTable In vechea ierarhie: Dictionary - Hashtable - Properties Evident, implementarea interfetelor este explicit realizat˘ la nivelul super- ¸ a claselor abstracte, acestea oferind de altfel ¸i implement˘ri concrete pentru s a multe din metodele definite de interfete. ¸ In general, clasele care descriu colectii au unele tr˘saturi comune, cum ar ¸ a fi: • 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 ¸ a ArrayList ¸i LinkedList, prima fiind ˆ general mult mai folosit˘. De ce s ın a exist˘ atunci ¸i clasa LinkedList ? Raspunsul const˘ ˆ faptul c˘ folosind a s a ın a reprezent˘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 s necesar actualiz˘rii colectiei ˆ cazul unor modific˘ri. a ¸ ın a S˘ consider˘m un exemplu ce creeaza o list˘ folosind ArrayList, respectiv a a a LinkedList ¸i execut˘ diverse operatii pe ea, cronometrˆnd timpul necesar s a ¸ a realiz˘rii acestora: a Listing 7.1: Comparare ArrayList - LinkedList import java . util .*; public class TestEficienta { final static int N = 100000; public static void testAdd ( List lst ) { long t1 = System . curre ntTimeM illis () ; for ( int i =0; i < N ; i ++) lst . add ( new Integer ( i ) ) ; long t2 = System . curre ntTimeM illis () ; System . out . println ( " Add : " + ( t2 - t1 ) ) ; }
  • 170.
    ˘ 7.4. FOLOSIREA EFICIENTAA 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 a ArrayList ofer˘ acces ˆ timp constant la elementele sale ¸i din acest motiv a ın s folosirea lui ”get” este rapid˘, ˆ timp ce pentru LinkedList este extrem a ın de lent˘, deoarece ˆ a ıntr-o list˘ ˆ a ınlantuit˘ accesul la un element se face prin ¸ a
  • 171.
    170 CAPITOLUL 7. COLECTII ¸ parcurgerea secvential˘ a listei pˆn˘ la elementul respectiv. ¸ a a a La operatiunea de eliminare, folosirea lui ArrayList este lent˘ deoarece el- ¸ a ementele r˘mase sufer˘ un proces de reindexare (shift la stˆnga), ˆ timp a a a ın ce pentru LinkedList este rapid˘ ¸i se face prin simpla schimbare a unei a s leg˘turi. Deci, ArrayList se comport˘ bine pentru cazuri ˆ care avem a a ın nevoie de reg˘sirea unor elemente la pozitii diferite ˆ list˘, iar LinkedList a ¸ ın a functioneaza eficient atunci cˆnd facem multe operatii de modificare (¸tergeri, ¸ a ¸ s inser˘ri). a Concluzia nu este c˘ una din aceste clase este mai ”bun˘” decˆt cealalt˘, a a a a ci c˘ exist˘ diferente substantiale ˆ reprezentarea ¸i comportamentul diferitelor a a ¸ ¸ ın s implement˘ri ¸i c˘ alegerea unei anumite clase pentru reprezentarea unei a s a multimi de elemente trebuie s˘ se fac˘ ˆ functie de natura problemei ce ¸ a a ın ¸ trebuie rezolvat˘.a 7.5 Algoritmi polimorfici Algoritmii polimorfici descri¸i ˆ aceast˘ sectiune sunt metode definite ˆ s ın a ¸ ın clasa Collections care permit efectuarea unor operatii utile cum ar fi c˘utarea, ¸ a sortarea, etc. Caracterisiticile principale ale acestor algoritmi sunt: • sunt metode de clas˘ (statice); a • au un singur argument de tip colectie; ¸ • apelul lor general va 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 generice Tipurile generice, introduse ˆ versiunea 1.5 a limbajului Java, simplific˘ ın a lucrul cu colectii, permitˆnd tipizarea elementelor acestora. Definirea unui ¸ ¸a tip generic se realizeaz˘ prin specificarea ˆ a ıntre paranteze unghiulare a unui tip de date Java, efectul fiind impunerea tipului respectiv pentru toate elementele colectiei: <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 ¸ s am dori ca elementele s˘ fie doar numere ˆ a ıntregi. Mai mult, trebuie s˘ facem a cast explicit de la tipul Object la Integer atunci cˆnd prelu˘m valoarea a a unui 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 ¸ a plificat˘ a secventei initiale: a ¸ ¸ // Dupa 1.5, folosind si autoboxing ArrayList<Integer> list = new ArrayList<Integer>(); list.add(123); int val = list.get(0); In cazul folosirii tipurilor generice, ˆ ıncercarea de a utiliza ˆ cadrul unei ın colectii a unui element necorespunz˘tor ca tip va produce o eroare la compi- ¸ a lare, spre deosebire de varianta anterioar˘ ce permitea doara aruncarea unor a exceptie de tipul ClassCastException ˆ cazul folosirii incorecte a tipurilor. ¸ ın 7.7 Iteratori ¸i enumer˘ri s a Enumer˘rile ¸i iteratorii descriu modalit˘¸i pentru parcurgerea secvential˘ a a s at ¸ a unei colectii, indiferent dac˘ aceasta este indexat˘ sau nu. Ei sunt descri¸i ¸ a a s de obiecte ce implementeaz˘ interfetele Enumeration, respectiv Iterator a ¸ sau ListIterator. Toate clasele care implementeaz˘ colectii au metode ce a ¸ returneaz˘ o enumerare sau un iterator pentru parcurgerea elementelor lor. a Deoarece functionalitatea interfetei Enumeration se reg˘se¸te ˆ Iterator, ¸ ¸ a s ın aceasta din urm˘ este preferat˘ ˆ noile implement˘ri ale colectiilor. a a ın a ¸ Metodele uzuale ale acestor interfete sunt prezentate mai jos, ˆ ¸ ımpreun˘ a cu 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 ın bele sensuri. Iteratorii sunt preferati enumer˘rilor datorit˘ posibilit˘¸ii lor ¸ a a at de a actiona asupra colectiei pe care o parcurg prin metode de tip remove, ¸ ¸ add, set dar ¸i prin faptul c˘ denumirile metodelor sunt mai concise. s a Atentie¸ Deoarece colectiile sunt construite peste tipul de date Object, metodele ¸ de tip next sau prev ale iteratorilor vor returna tipul Object, fiind respon- sabilitatea noastr˘ de a face conversie (cast) la alte tipuri de date, dac˘ este a a cazul.
  • 175.
    174 CAPITOLUL 7. COLECTII ¸ In exemplul de mai jos punem ˆ ıntr-un vector numerele de la 1 la 10, le amestec˘m, dup˘ care le parcurgem element cu element folosind un iterator, a a ˆ ınlocuind numerele pare cu 0. Listing 7.2: Folosirea unui iterator import java . util .*; class TestIterator { public static void main ( String args []) { ArrayList a = new ArrayList () ; // Adaugam numerele de la 1 la 10 for ( int i =1; i <=10; i ++) a . add ( new Integer ( i ) ) ; // Amestecam elementele colectiei Collections . shuffle ( a ) ; System . out . println ( " Vectorul amestecat : " + a ) ; // Parcurgem vectorul for ( ListIterator it = a . listIterator () ; it . hasNext () ; ) { Integer x = ( Integer ) it . next () ; // Daca elementul curent este par , il facem 0 if ( x . intValue () % 2 == 0) it . set ( new Integer (0) ) ; } System . out . print ( " Rezultat : " + a ) ; } } Incepˆnd cu versiunea 1.5 a limbajului Java, exist˘ o variant˘ simplificat˘ a a a a de utilizare a iteratorilor. Astfel, o secvent˘ de genul: ¸a ArrayList<Integer> list = new ArrayList<Integer>(); for (Iterator i = list.iterator(); i.hasNext();) { Integer val=(Integer)i.next(); // Proceseaza val ...
  • 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 8 Serializarea obiectelor 8.1 Folosirea serializ˘rii a Definitie¸ Serializarea este o metod˘ ce permite transformarea unui obiect ˆ a ıntr-o secventa de octeti sau caractere din care s˘ poat˘ fi ref˘cut ulterior obiectul ¸˘ ¸ a a a original. Cu alte cuvinte, serializarea permite salvarea ˆ ıntr-o manier˘ unitar˘ a a a tuturor informatiilor unui obiect pe un mediu de stocare extern programului. ¸ Procesul invers, de citire a unui obiect serializat pentru a-i reface starea original˘, se nume¸te deserializare. Intr-un cadru mai larg, prin serializare a s se ˆ ı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 a obiect 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 a de obiectul care se serializeaz˘ pot referi la rˆndul lor alte obiecte, ¸i a¸a mai a a s s departe. 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 a unui obiect nu este tocmai facil. In cazul ˆ care starea unui obiect este format˘ doar din valori ale unor ın a variabile de tip primitiv, atunci salvarea informatiilor ˆ ¸ ıncapsulate ˆ acel ın obiect se poate face ¸i prin salvarea pe rˆnd a datelor, folosind clasa s a DataOutputStream, pentru ca apoi s˘ fie restaurate prin metode ale clasei a DataInputStream, 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 a membre ale obiectului pot fi instante ale altor obiecte, unele cˆmpuri pot ¸ a face referint˘ la acela¸i obiect, etc. ¸a s Serializarea ˆ format binar a tipurilor primitive ¸i a obiectelor se real- ın s izeaz˘ prin intermediul fluxurilor definite de clase specializate ˆ acest scop a ın cu ar fi: ObjectOutputStream pentru scriere ¸i ObjectInputStream pentru restau- s rare. In continuare, prin termenul serializare ne vom referi doar la serializarea ˆ format binar. ın 8.1.1 Serializarea tipurilor primitive Serializarea tipurilor primitive poate fi realizat˘ fie prin intermediul fluxu- a rilor 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 a writeTipPrimitiv pentru scrierea/citirea datelor primitive ¸i a ¸irurilor de s s caractere. Mai jos este prezentat un exemplu de serializare folosind clasa DataOutputStream: FileOutputStream fos = new FileOutputStream("test.dat"); DataOutputStream out = new DataOutputStream(fos); out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); out.flush(); fos.close(); Citirea informatiilor scrise ˆ exemplul de mai sus se va face astfel: ¸ ın FileInputStream fis = new FileInputStream("test.dat"); DataInputStream in = new DataInputStream(fis); int i = in.readInt(); double d = in.readDouble(); boolean b = in.readBoolean();
  • 181.
    180 CAPITOLUL 8. SERIALIZAREA OBIECTELOR String s = in.readUTF(); fis.close(); 8.1.2 Serializarea obiectelor Serializarea obiectelor se realizeaz˘ prin intermediul fluxurilor definite de a clasele ObjectOutputStream (pentru salvare) ¸i ObjectInputStream s (pentru restaurare). Acestea sunt fluxuri de procesare, ceea ce ˆ ınseamna c˘ vor fi folosite ˆ a ımpreuna cu alte fluxuri pentru scrierea/citirea efectiv˘ a a datelor pe mediul extern pe care va fi salvat, sau de pe care va fi restaurat un obiect serializat. Mecanismul implicit de serializare a unui obiect va salva numele clasei obiectului, signatura clasei ¸i valorile tuturor cˆmpurile serializabile ale obiec- s a tului. Referintele la alte obiecte serializabile din cadrul obiectului curent vor ¸ duce automat la serializarea acestora iar referintele multiple c˘tre un acela¸i ¸ a s obiect 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 a exista ¸i metode pentru scrierea/citirea datelor primitive ¸i a ¸irurilor de s s s caractere. Metodele pentru serializarea obiectelor sunt: • writeObject, pentru scriere ¸i s • readObject, pentru restaurare. 8.1.3 Clasa ObjectOutputStream Scrierea obiectelor pe un flux de ie¸ire este un proces extrem de simplu, s secventa 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 ın fi¸ierul test.ser, ˆ s ımpreun˘ cu un obiect de tip String. Evident, fi¸ierul a s rezultat va contine informatiile reprezentate ˆ format binar. ¸ ¸ ın FileOutputStream fos = new FileOutputStream("test.ser"); ObjectOutputStream out = new ObjectOutputStream(fos); out.writeObject("Ora curenta:"); out.writeObject(new Date()); out.flush(); fos.close(); Deoarece implementeaz˘ interfata DataOutput, pe lˆnga metoda de scriere a ¸ a a obiectelor, clasa pune la dispozitie ¸i metode de tipul writeTipPrimitiv s pentru serializarea tipurilor de date primitive ¸i a ¸irurilor de caractere, ast- s s fel ˆ at apeluri ca cele de mai jos sunt permise : ıncˆ out.writeInt(12345); out.writeDouble(12.345); out.writeBoolean(true); out.writeUTF("Sir de caractere"); Metoda writeObject arunc˘ exceptii de tipul IOException ¸i derivate a ¸ s din aceasta, mai precis NotSerializableException dac˘ obiectul primit ca a argument nu este serializabil, sau InvalidClassException dac˘ sunt prob- a leme cu o clas˘ necesar˘ ˆ procesul de serializare. Vom vedea ˆ continuare a a ın ın c˘ un obiect este serializabil dac˘ este instant˘ a unei clase ce implementeaz˘ a a ¸a a interfata Serializable. ¸ 8.1.4 Clasa ObjectInputStream Odat˘ ce au fost scrise obiecte ¸i tipuri primitive de date pe un flux, citirea a s acestora ¸i reconstruirea obiectelor salvate se va face printr-un flux de intrare s de tip ObjectInputStream. Acesta este de asemenea un flux de procesare ¸i va trebui asociat cu un flux pentru citirea efectiv˘ a datelor, cum ar fi s a FileInputStream pentru date salvate ˆ ıntr-un fi¸ier. Secventa uzual˘ pentru s ¸ a deserializare 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 a ce ˆ ınseamn˘ c˘ trebuie realizat˘ explicit conversia la tipul corespunzator a a a obiectului citit: Date date = in.readObject(); // gresit Date date = (Date)in.readObject(); // corect Atentie¸ Ca ¸i la celelalte 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 ın acestea au fost scrise, altfel vor ap˘rea evident exceptii ˆ procesul de dese- a ¸ ın rializare. Clasa ObjectInputStream implementeaz˘ interfata DataInput deci, pe a ¸ lˆng˘ metoda de citire a obiectelor, clasa pune la dispozitie ¸i metode de a a ¸ s tipul readTipPrimitiv pentru citirea tipurilor de date primitive ¸i a ¸irurilor s s de caractere. int i = in.readInt(); double d = in.readDouble(); boolean b = in.readBoolean(); String s = in.readUTF();
  • 184.
    8.2. OBIECTE SERIALIZABILE 183 8.2 Obiecte serializabile Un obiect este serializabil dac˘ ¸i numai dac˘ clasa din care face parte im- as a plementeaz˘ 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 a sau indirect, interfata Serializable. ¸ 8.2.1 Implementarea interfetei Serializable ¸ Interfata Serializable nu contine nici o declaratie de metod˘ sau constant˘, ¸ ¸ ¸ a a singurul ei scop fiind de a identifica clasele ale c˘ror obiecte sunt serializabile. a Definitia sa complet˘ este: ¸ a package java.io; public interface Serializable { // Nimic ! } Declararea claselor ale c˘ror instante trebuie s˘ fie serializate este a¸adar a ¸ a s extrem 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 a sa 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ˆ a interfata Serializable atunci va fi generat˘ o exceptie de tipul NotSerializableException ¸ a ¸ ce va identifica respectiva clas˘ neserializabil˘. a a 8.2.2 Controlul serializ˘rii a Exist˘ cazuri cˆnd dorim ca unele variabile membre ale unui obiect s˘ nu fie a a a salvate automat ˆ procesul de serializare. Acestea sunt cazuri comune atunci ın cˆ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 a private ˆ cadrul clasei aceste cˆmpuri particip˘ la serializare. Pentru ca un ın a a cˆmp s˘ nu fie salvat ˆ procesul de serializare el trebuie declarat cu modi- a a ın ficatorul transient ¸i trebuie s˘ fie ne-static. De exemplu, declararea unei s a variabile membre temporare ar trebui facut˘ astfel: a transient private double temp; // Ignorata la serializare Modificatorul static anuleaz˘ efectul modificatorului transient. Cu a alte 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 a cele marcate ’NU’, nu particip˘ iar cele marcate cu ’Exceptie’ vor provoca a exceptii de tipul NotSerializableException. ¸ Listing 8.1: Modificatorii static ¸i transient s import 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 neserializabili import java . io .*; class A { int x =1; } class B implements Serializable { int y =2; } public class Test2 implements Serializable { A a = new A () ; // Exceptie B b = new B () ; // DA public String toString () { return a . x + " , " + b . y ; } } Atunci cˆnd o clas˘ serializabila deriva dintr-o alt˘ clas˘, salvarea cˆmpurilor a a a a a clasei p˘rinte se va face doar dac˘ ¸i aceasta este serializabil˘. In caz contrar, a as a subclasa trebuie s˘ salveze explicit ¸i cˆmpurile mo¸tenite. a s a s Listing 8.3: Serializarea cˆmpurilor mo¸tenite a s import java . io .*; class C { int x =0; // Obligatoriu constructor fara argumente } class D extends C implements Serializable { int y =0; } public class Test3 extends D { public Test3 () { x = 1; // NU y = 2; // DA
  • 187.
    186 CAPITOLUL 8. SERIALIZAREA OBIECTELOR } public String toString () { return x + " , " + y ; } } Mai jos este descrisa o aplicatie care efectueaz˘ salvarea ¸i restaurarea ¸ a s unor obiecte din cele trei clase prezentate mai sus. Listing 8.4: Testarea serializ˘rii a import java . io .*; public class Exemplu { public static void test ( Object obj ) throws IOException { // Salvam FileOutputStream fos = new FileOutputStream ( " fisier . ser " ) ; ObjectOutputStrea m out = new Ob je ct Ou tp utS tr ea m ( fos ) ; out . writeObject ( obj ) ; out . flush () ; fos . close () ; System . out . println ( " A fost salvat obiectul : " + obj ) ; // Restauram FileInputStream fis = new FileInputStream ( " fisier . ser " ) ; ObjectInputStream in = new Obje ctInput Stream ( fis ) ; try { obj = in . readObject () ; } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } fis . close () ; System . out . println ( " A fost restaurat obiectul : " + obj ) ; } public static void main ( String args []) throws IOException { test ( new Test1 () ) ; try { test ( new Test2 () ) ; } catch ( N o t Se r i a l i z a b l e E x c e p t i o n e ) {
  • 188.
    ˘ 8.3. PERSONALIZAREA SERIALIZARIIOBIECTELOR 187 System . out . println ( " Obiect neserializabil : " + e ) ; } test ( new Test3 () ) ; } } Rezultatul acestui program va fi : A fost salvat obiectul: 1, 2, 3, 4 A fost restaurat obiectul: 1, 0, 3, 4 Obiect neserializabil: java.io.NotSerializableException: A A fost salvat obiectul: 1, 2 A fost restaurat obiectul: 0, 2 8.3 Personalizarea serializ˘rii obiectelor a Dezavantajul mecanismului implicit de serializare este c˘ algoritmul pe care a se beazeaz˘, fiind creat pentru cazul general, se poate comporta ineficient ˆ a ın anumite situatii: poate fi mult mai lent decˆt este cazul sau reprezentarea ¸ a binar˘ generat˘ pentru un obiect poate fi mult mai voluminoas˘ decˆt ar a a a a trebui. In aceste situatii, putem s˘ ˆ ¸ a ınlocuim algoritmul implicit cu unul propriu, particularizat pentru o clas˘ anume. De asemenea, este posibil a s˘ extindem comportamentul implicit, 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˘ a am 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- a izabil˘!) a metodelor writeObject ¸i readObject avˆnd exact signatura de a s a mai jos: private void writeObject(java.io.ObjectOutputStream stream) throws IOException private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException Metoda writeObject controleaz˘ ce date sunt salvate iar readObject a controleaz˘ modul ˆ care sunt restaurate obiectele, citind informatiile salvate a ın ¸ ¸i, eventual, modifcˆnd starea obiectelor citite astfel ˆ at ele s˘ corespund˘ s a ıncˆ a a
  • 189.
    188 CAPITOLUL 8. SERIALIZAREA OBIECTELOR anumitor cerinte. In cazul ˆ care nu dorim s˘ ˆ ¸ ın a ınlocuim complet mecanis- mul standard, putem s˘ folosim metodele defaultWriteObject, respectiv a defaultReadObject care descriu procedurile implicite de serializare. Forma general˘ de implementare a metodelor writeObject ¸i readObject a s este: private void writeObject(ObjectOutputStream stream) throws IOException { // Procesarea campurilor clasei (criptare, etc.) ... // Scrierea obiectului curent stream.defaultWriteObject(); // Adaugarea altor informatii suplimentare ... } private void readObject(ObjectInputStream stream) throws IOException,ClassNotFoundException { // Restaurarea obiectului curent stream.defaultReadObject(); // Actualizarea starii obiectului (decriptare, etc.) // si extragerea informatiilor suplimentare ... } Metodele writeObject ¸i readObject sunt responsabile cu serializarea s clasei ˆ care sunt 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 s prie cu serializarea superclasei sale, atunci trebuie s˘ implementeze interfata a ¸ Externalizable. 8.3.1 Controlul versiunilor claselor S˘ 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 SERIALIZARIIOBIECTELOR 189 notiunea de angjat. O variant˘ simplificat˘ a acesteia ar putea ar˘ta astfel: ¸ a a a Listing 8.5: Prima variant˘ a clasei Angajat a import java . io .*; class Angajat implements Serializable { public String nume ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } } Mai jos este prezentat˘ o mic˘ aplicatie care permite introducerea de a a ¸ angajati ¸i salvarea lor ˆ ¸ s ıntr-un 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 ¸a angajatilor cu noi persoane. ¸ Listing 8.6: Aplicatia de gestionare a angajatilor ¸ ¸ import java . io .*; import java . util .*; public class GestiuneAngajati { // Lista angajatilor ArrayList ang = new ArrayList () ; public void citire () throws IOException { FileInputStream fis = null ; try { fis = new FileInputStream ( " angajati . ser " ) ; ObjectInputStream in = new Obje ctInput Stream ( fis ) ;
  • 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 SERIALIZARIIOBIECTELOR 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 a num˘r suficient de mare de angajati ˆ fi¸ier, clasa Angajat este modifi- a ¸ ın s cat˘ 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 ¸ ¸ ¸ s va 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 a tip transient. De ce se ˆ amplˆ acest lucru ? ıntˆ a Explicatia const˘ ˆ faptul c˘ mecanismul de serializare Java este foarte ¸ a ın a atent cu signatura claselor serializate. Pentru fiecare obiect serializat este calculat 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 a este 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˘ a cu celelalte date. In plus, orice modificare semnificativ˘ a clasei, cum ar a fi ad˘ugarea unui nou cˆmp, va determina modificarea num˘rului s˘u de a a a a versiune. La restaurarea unui obiect, num˘rul de versiune salvat ˆ forma serializat˘ a ın a va fi reg˘sit ¸i comparat cu noua semn˘tur˘ a clasei obiectului. In cazul ˆ a s a a ın care 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 a unor anomalii ce pot ap˘rea cˆnd dou˘ versiuni de clase sunt incompati- a a a bile, dat poate fi sup˘r˘toare atunci cˆnd modific˘rile aduse clasei nu stric˘ aa a a a compatibilitatea cu vechea versiune. In aceast˘ situatie trebuie s˘ comu- a ¸ a nic˘m explicit c˘ cele dou˘ clase sunt compatibile. Acest lucru se realizeaz˘ a a a a prin setarea manual˘ a variabilei serialVersionUID ˆ cadrul clasei dorite, a ın ad˘ugˆnd pur ¸i simplu cˆmpul: a a s a
  • 193.
    192 CAPITOLUL 8. SERIALIZAREA OBIECTELOR static final long serialVersionUID = /* numar_serial_clasa */; Prezenta variabilei serialVersionUID printre membrii unei clase va in- ¸ forma algoritmul de serialzare c˘ nu mai calculeze num˘rul de serie al clasei, a a ci s˘-l foloseasc˘ pe cel specificat de noi. Cum putem afla ˆ a num˘rul de se- a a ıns˘ a rie al vechii clase Angajat care a fost folosit˘ anterior la salvarea angajatilor a ¸ ? Utilitarul serialVer permite generarea num˘rului serialVersionUID pen- a tru 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 a Angajat: static final long serialVersionUID = 5653493248680665297L; Vom rescrie noua clas˘ Angajat astfel ˆ at s˘ fie compatibil˘ cu cea a ıncˆ a a veche astfel: Listing 8.7: Variant˘ compatibil˘ a clasei Angajat a a import java . io .*; class Angajat implements Serializable { static final long serialVersionUID = 56 534 93 248 680 665 29 7 L ; public String nume , adresa ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . adresa = " Iasi " ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } } Aplicatia noastr˘ va functiona acum, ˆ a rubrica adres˘ nu va 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 SERIALIZARIIOBIECTELOR 193 salvare a datelor, vor fi serializate ¸i informatiile legate de adres˘ (evident, s ¸ a trebuie ˆ a s˘ le citim de la tastatur˘...) ıns˘ a a 8.3.2 Securizarea datelor Dup˘ cum am 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 ın binar, informatiile unui obiect serializat nu sunt criptate ˆ nici un fel ¸i pot ¸ ın s fi reg˘site cu u¸urint˘, ceea ce poate reprezenta un inconvenient atunci cˆnd a s ¸a a exist˘ cˆmpuri confidentiale. a a ¸ Rezolvarea acestei probleme se face prin modificarea mecanismului im- plicit de serializare, implementˆnd metodele readObject ¸i writeObject, a s precum ¸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 a import java . io .*; class Angajat implements Serializable { static final long serialVersionUID = 5 65 349 324 868 06 652 97 L ; public String nume , adresa ; public int salariu ; private String parola ; public Angajat ( String nume , int salariu , String parola ) { this . nume = nume ; this . adresa = " Iasi " ; this . salariu = salariu ; this . parola = parola ; } public String toString () { return nume + " ( " + salariu + " ) " ; } static String criptare ( String input , int offset ) { StringBuffer sb = new StringBuffer () ; for ( int n =0; n < input . length () ; n ++) sb . append (( char ) ( offset + input . charAt ( n ) ) ) ; return sb . toString () ;
  • 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- a buie s˘ implementeze interfata Externalizable. Pentru instante ale acestor a ¸ ¸ clase doar numele clasei este salvat automat pe fluxul de obiecte, clasa fiind responsabil˘ cu scrierea ¸i citirea membrilor s˘i ¸i trebuie s˘ se coordoneze a s a s a cu 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 a External ¸i readExternal ˆ care se va face serializarea complet˘ a obiectelor s ın a ¸i coordonarea cu superclasa ei. s Uzual, interfata Externalizable este folosit˘ ˆ situatii ˆ care se dore¸te ¸ a ın ¸ ın s ˆ ımbun˘t˘¸irea performantelor algoritmului standard, mai exact cre¸terea vitezei a at ¸ s procesului de serializare.
  • 196.
    ˘ 8.3. PERSONALIZAREA SERIALIZARIIOBIECTELOR 195 Mai jos este prezentat˘ o clas˘ simpl˘ ¸i modalitatea de rescriere a sa a a a s folosind interfata Externalizable: ¸ Listing 8.9: Serializare implicit˘ a import java . io .*; class Persoana implements Serializable { int cod ; String nume ; public Persoana ( String nume , int cod ) { this . nume = nume ; this . cod = cod ; } } Listing 8.10: Serializare proprie import java . io .*; class Persoana implements Externalizable { int cod ; String nume ; public Persoana ( String nume , int cod ) { this . nume = nume ; this . cod = cod ; } public void writeExternal ( ObjectOutput s ) throws IOException { s . writeUTF ( nume ) ; s . writeInt ( cod ) ; } public void readExternal ( ObjectInput s ) throws ClassNotFoundException , IOException { nume = s . readUTF () ; cod = s . readInt () ; } }
  • 197.
    196 CAPITOLUL 8. SERIALIZAREA OBIECTELOR 8.4 Clonarea obiectelor Se ¸tie c˘ nu putem copia valoarea unui obiect prin instructiunea de atribuire. s a ¸ O secventa de forma: ¸ TipReferinta o1 = new TipReferinta(); TipReferinta o2 = o1; nu face 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 clone definit˘ ˆ clasa Object. Aceasta creeaz˘ un nou obiect ¸i initializeaz˘ toate a ın a s ¸ a variabilele sale membre cu valorile obiectului clonat. TipReferinta o1 = new TipReferinta(); TipReferinta o2 = (TipReferinta) o1.clone(); 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 a referinta 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 ımpreuna cu copierea tuturor obiectelor referite de cˆmpurile acelui obiect poate fi a implementat˘ prin mecanismul serializ˘rii astfel: a a public Object clone() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(baos); out.writeObject(this); out.close(); byte[] buffer = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(buffer); ObjectInputStream in = new ObjectInputStream(bais); Object ret = in.readObject(); in.close(); return ret; } catch (Exception e) {
  • 198.
    8.4. CLONAREA OBIECTELOR 197 System.out.println(e); return null; } }
  • 199.
    198 CAPITOLUL 8. SERIALIZAREA OBIECTELOR
  • 200.
    Capitolul 9 Interfata grafic˘cu utilizatorul ¸ a 9.1 Introducere Interfata grafic˘ cu utilizatorul (GUI), este un termen cu ˆ ¸eles larg care ¸ a ınt se refer˘ la toate tipurile de comunicare vizual˘ ˆ a a ıntre un program ¸i utiliza- s torii s˘i. Aceasta este o particularizare a interfetei cu utilizatorul (UI), prin a ¸ care vom ˆ ıntelege conceptul generic de interactiune dintre program ¸i utiliza- ¸ s tori. Limbajul Java pune la dispozitie numeroase clase pentru implementarea ¸ diverselor functionalitati UI, ˆ a ne vom ocupa ˆ continuare de cele care ¸ ıns˘ ın permit realizarea intefetei grafice cu utilizatorul (GUI). ¸ De la aparitia limbajului Java, bibliotecile de clase care ofer˘ servicii ¸ a grafice au suferit probabil cele mai mari schimb˘ri ˆ trecerea de la o ver- a ın siune la alta. Acest lucru se datoreaz˘, pe de o parte dificult˘¸ii legate de a at implementarea notiunii de portabilitate, pe de alt˘ parte nevoii de a integra ¸ a mecanismele GUI cu tehnologii ap˘rute ¸i dezvoltate ulterior, cum ar fi Java a s Beans. 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 ¸ a Swing, 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 as evenimentelor din AWT, deoarece va fi simplificat procesul de ˆ ¸elegere a ınt dezvolt˘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. s 9.2 Modelul AWT Pachetul care ofer˘ componente AWT este java.awt. a Obiectele grafice sunt derivate din Component, cu exceptia meniurilor care ¸ descind din clasa MenuComponent. A¸adar, prin notiunea de component˘ s ¸ a vom ˆ ıntelege ˆ continuare orice obiect care poate avea o reprezentare grafic˘ ın a ¸i care poate interactiona cu utilizatorul. Exemple de componente sunt fere- s strele, butoanele, listele, bare de defilare, etc. Toate componentele AWT sunt definte de clase proprii ce se gasesc ˆ pachetul java.awt, clasa Component ın fiind superclasa abstract˘ a tuturor acestor clase. a Crearea obiectelor grafice nu realizeaz˘ automat ¸i afi¸area lor pe ecran. a s s Mai ˆ 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 201 sau 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 ¸a se mai nume¸te container ¸i reprezint˘ o instant˘ a unei clase derivate din s s a ¸a Container. Clasa Container este o subclas˘ aparte a lui Component, fiind a la 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 ın momentul ˆ care utilizatorul a efectuat o actiune ¸i, prin urmare, compo- ın ¸ s nentele 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 a siunea 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. In schimb, ˆ ıntr-un program trebuie specificat codul care se execut˘ la aparitia a ¸ unui eveniment. Tratarea evenimentelor se realizeaz˘ prin intermediul unor a clase de tip listener (ascult˘tor, consumator de evenimente), clase care sunt a definite ˆ pachetul java.awt.event. In Java, orice component˘ poate ”con- ın a suma” evenimentele generate de o alt˘ component˘ (vezi ”Tratarea eveni- a a mentelor”). S˘ consider˘m un mic exemplu, ˆ care cre˘m o fereastr˘ ce contine dou˘ a a ın a a ¸ a butoane. Listing 9.1: O fereastr˘ cu dou˘ butoane a a import java . awt .*; public class ExempluAWT1 { public static void main ( String args []) { // Crearea ferestrei - un obiect de tip Frame Frame f = new Frame ( " O fereastra " ) ; // Setarea modului de dipunere a componentelor f . setLayout ( new FlowLayout () ) ; // Crearea celor doua butoane Button b1 = new Button ( " OK " ) ; Button b2 = new Button ( " Cancel " ) ; // Adaugarea butoanelor f . add ( b1 ) ;
  • 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 a de noi cˆt ¸i butonul de ˆ a s ınchidere a ferestrei sunt functionale, adic˘ pot fi ¸ a apasate, dar nu realizeaz˘ nimic. Acest lucru se ˆ ampl˘ deoarece nu am a ıntˆ a specificat nic˘ieri codul care trebuie s˘ se execute la ap˘sarea acestor bu- a a a toane. De asemenea, mai trebuie remarcat c˘ nu am specificat nic˘ieri dimensiu- a a nile ferestrei sau ale butoanelor ¸i nici pozitiile ˆ acestea s˘ fie plasate. Cu s ın a toate acestea ele sunt plasate unul lˆnga celalalt, f˘r˘ s˘ se suprapun˘ iar a aa a a suprafata fereastrei este suficient de mare cˆt s˘ cuprind˘ ambele obiecte. ¸ a a a Aceste ”fenomene” sunt provocate de un obiect special de tip FlowLayout pe care l-am specificat ¸i care se ocup˘ cu gestionarea ferestrei ¸i cu plasarea s a s componentelor ˆ ıntr-o anumit˘ ordine pe suprafata ei. A¸adar, modul de a ¸ s aranjare nu este o caracteristic˘ a suprafetei de afi¸are ci, fiecare container a ¸ s are asociat un obiect care se ocup˘ cu dimensionarea ¸i dispunerea compo- a s nentelor pe suprafata de afi¸are ¸i care se numeste gestionar de pozitionare ¸ s s ¸ (layout manager) (vezi ”Gestionarea pozition˘rii”). ¸ a 9.2.1 Componentele AWT Dup˘ cum am spus deja, toate componentele AWT sunt definte de clase a proprii ce se gasesc ˆ pachetul java.awt, clasa Component fiind superclasa ın abstracta a tuturor acestor clase. • Button - butoane cu eticheta format˘ dintr-un text pe o singur˘ linie; a a • Canvas - suprafat˘ pentru desenare; ¸a • Checkbox - component˘ ce poate avea dou˘ 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 ¸ ın platforme de operare, acestea nu au putut fi integrate ca obiecte de tip Component, superclasa care descrie meniuri fiind MenuComponent (vezi ”Meniuri”). Componentele AWT au peste 100 de metode comune, mo¸tenite din clasa s Component. Acestea servesc uzual pentru aflarea sau setarea atributelor obiectelor, cum ar fi: dimensiune, pozitie, culoare, font, etc. ¸i au formatul ¸ s general getProprietate, respectiv setProprietate. Cele mai folosite, grupate pe 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 isEnabled 9.2.2 Suprafete de afi¸are (Clasa Container) ¸ s Crearea 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 ¸ a va 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 a este prezentat˘ mai jos: a • Window - este superclasa tututor ferestrelor. Din aceast˘ clas˘ sunt a a derivate: – Frame - ferestre standard; – Dialog - ferestre de dialog modale sau nemodale; • Panel - o suprafat˘ 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 ın cadrul containerului. Dac˘ nu este specificat nici un index la ad˘ugarea unei a a componente, 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. ¸ ¸ s Dintre 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 ¸ a S˘ consider˘m mai ˆ ai un exemplu de program Java care afi¸eaz˘ 5 butoane a a ıntˆ s a pe o fereastr˘: a Listing 9.2: Pozitionarea a 5 butoane ¸ import java . awt .*; public class TestLayout { public static void main ( String args []) { Frame f = new Frame ( " Grid Layout " ) ; f . setLayout ( new GridLayout (3 , 2) ) ; // * Button 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 aa restul 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 a dispunere 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 a gestionari 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 ¸ s nu este o caracteristic˘ a containerului. Fiecare obiect de tip Container a (Applet, Frame, Panel, etc.) are asociat un obiect care se ocup˘ cu dis- a punerea componentelor pe suprafata sa ¸i anume gestionarul s˘u de pozitionare. ¸ s a ¸ Toate clasele care instantiaza obiecte pentru gestionarea pozition˘rii imple- ¸ ¸ a menteaz˘ interfat˘ LayoutManager. a ¸a La instantierea unui container se creeaz˘ implicit un gestionar de pozitionare ¸ a ¸ asociat acestuia. De exemplu, pentru o fereastr˘ gestionarul implict este de a tip BorderLayout, ˆ timp ce pentru un panel este de tip FlowLayout. ın 9.3.1 Folosirea gestionarilor de pozitionare ¸ 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 ¸ s automat la crearea sa. In cazul ˆ care acesta nu corespunde necesit˘¸ilor ın at noastre, 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 parametru orice instant˘ a unei clase care implementeaz˘ interfat˘ LayoutManager. ¸a a ¸a Secventa de ata¸are a unui gestionar pentru un container, particularizat˘ ¸ s a pentru FlowLayout, este: FlowLayout gestionar = new FlowLayout(); container.setLayout(gestionar); // sau, mai uzual: container.setLayout(new FlowLayout()); Programele nu apeleaz˘ ˆ general metode ale gestionarilor de pozitionare, a ın ¸ dar ˆ cazul 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- a imesionat. Pozitiile ¸i dimensiunile componentelor nu sunt fixe, ele fiind ¸ s ajustate automat de c˘tre gestionar la fiecare redimensionare astfel ˆ at s˘ a ıncˆ a ocupe 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 a ferredSize, getMinimumSize ¸i getMaximumSize care s˘ returneze di- s a mensiunea implicit˘ a componentei respective ¸i limitele ˆ afara c˘rora com- a s ın a ponenta 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 a un gestionar aceast˘ pozitionare absolut˘ a componentelor nu este posibil˘ ¸i a ¸ a as deci trebuie cumva s˘ renunt˘m la gestionarea automat˘ a containerul. Acest a ¸a a lucru se realizeaz˘ prin trimitera argumentului null metodei setLayout: a // pozitionare absoluta a componentelor in container container.setLayout(null); Folosind pozitionarea absolut˘, nu va mai fi ˆ a suficient s˘ ad˘ugam cu ¸ a ıns˘ a a metoda add componentele ˆ container, ci va trebui s˘ specific˘m pozitia ¸i ın a a ¸ s
  • 210.
    ¸ ˘ 9.3. GESTIONAREA POZITIONARII 209 dimensiunea 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 ¸ ın situatiile cˆnd acest lucru este posibil, deoarece permit programului s˘ aib˘ ¸ a a a aceea¸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 FlowLayout Acest gestionar a¸eaz˘ componentele pe suprafata de afi¸are ˆ flux liniar, mai s a ¸ s ın precis, 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 a trece la urm˘toarea linie, de sus ˆ jos. Ad˘ugarea componentelor se face de a ın a la stˆnga la dreapta pe linie, iar alinierea obiectelor ˆ cadrul unei linii poate a ın fi de trei feluri: la stˆnga, la dreapta ¸i pe centru. Implicit, componentele a s sunt centrate pe fiecare linie iar distanta implicit˘ ˆ ¸ a ıntre componente este de 5 pixeli pe vertical˘ ¸i 5 pe orizontal˘. as a Este gestionarul implicit al containerelor derivate din clasa Panel deci ¸i s al applet-urilor. Listing 9.3: Gestionarul FlowLayout import java . awt .*; public class TestFlowLayout { public static void main ( String args []) { Frame f = new Frame ( " Flow Layout " ) ; f . setLayout ( new FlowLayout () ) ; Button b1 = new Button ( " Button 1 " ) ; Button b2 = new Button ( " 2 " ) ; Button b3 = new Button ( " Button 3 " ) ;
  • 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˘ a pe o linie, ultimele dintre ele vor fi trecute pe linia urm˘toare: a 9.3.3 Gestionarul BorderLayout Gestionarul BorderLayout ˆ ımparte suprafata de afi¸are ˆ cinci regiuni, core- ¸ s ın spunz˘toare celor patru puncte cardinale ¸i centrului. O component˘ poate fi a s a plasat˘ ˆ oricare din aceste regiuni, dimeniunea componentei fiind calculata a ın astfel ˆ at s˘ ocupe ˆ ıncˆ a ıntreg spatiul de afi¸are oferit de regiunea respectiv˘. ¸ s a Pentru a ad˘uga mai multe obiecte grafice ˆ a ıntr-una din cele cinci zone, ele trebuie grupate ˆ prealabil ˆ ın ıntr-un panel, care va fi amplasat apoi ˆ regiunea ın dorit˘ (vezi ”Gruparea componentelor - clasa Panel”). a A¸adar, la ad˘ugarea unei componente pe o suprafat˘ gestionat˘ de BorderLayout, s a ¸a a metoda add va mai primi pe lˆnga referinta componentei ¸i zona ˆ care a ¸ s ın aceasta va fi amplasat˘, care va fi specificat˘ prin una din constantele clasei: a a NORTH, 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 BorderLayout import java . awt .*; public class TestBorderLayout { public static void main ( String args []) { Frame f = new Frame ( " Border Layout " ) ; // Apelul de mai jos poate sa lipseasca f . setLayout ( new BorderLayout () ) ; f . add ( new 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 s sudul 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 a Redimensionarea componentelor din fiecare zon˘ se face astfel ˆ at ele ocup˘ a ıncˆ a toat˘ zona containerului din care fac parte. a 9.3.4 Gestionarul GridLayout Gestionarul GridLayout organizeaz˘ containerul ca un tabel cu rˆnduri ¸i a a s coloane, componentele fiind plasate ˆ celulele tabelului de la stˆnga la ın a dreapta, ˆ ıncepˆnd cu primul rˆnd. Celulele tabelului au dimensiuni egale a a iar o component˘ poate ocupa doar o singur˘ celul˘. Num˘rul de linii ¸i a a a a s coloane 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 linii s a a sau coloane este 0 (dar nu ambele ˆ acela¸i timp), atunci componentele vor ın s fi 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 GridLayout import java . awt .*; public class TestGridLayout { public static void main ( String args []) { Frame f = new Frame ( " Grid Layout " ) ; f . setLayout ( new GridLayout (3 , 2) ) ; f . add ( new 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 a coloane, 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 a 9.3.5 Gestionarul CardLayout Gestionarul 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 213 Suprafata 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 a ponent˘ 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 a ponent˘ 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 a spatiului disponibil ˆ situatii ˆ care componentele pot fi grupate ˆ a¸a ¸ ın ¸ ın ın s fel ˆ at utilizatorul s˘ interactioneze la un moment dat doar cu un anumit ıncˆ a ¸ grup (o carte din pachet), celelalte fiind ascunse. O clas˘ Swing care implementeaz˘ un mecansim similar este JTabbedPane. a a Listing 9.6: Gestionarul CardLayout import java . awt .*; import java . awt . event .*; public class TestCardLayout extends Frame implements ActionListener { Panel tab ; public TestCardLayout () { super ( " Test CardLayout " ) ; Button card1 = new Button ( " Card 1 " ) ; Button card2 = new Button ( " Card 2 " ) ; Panel butoane = new Panel () ; butoane . add ( card1 ) ; butoane . add ( card2 ) ; tab = new Panel () ; tab . setLayout ( new CardLayout () ) ; TextField tf = new TextField ( " Text Field " ) ; Button btn = new Button ( " Button " ) ; tab . add ( " Card 1 " , tf ) ; tab . add ( " Card 2 " , btn ) ; add ( butoane , BorderLayout . NORTH ) ; add ( tab , BorderLayout . CENTER ) ; pack () ; show () ;
  • 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˘ a 9.3.6 Gestionarul GridBagLayout Este cel mai complex ¸i flexibil gestionar de pozitionare din Java. La fel ca ˆ s ¸ ın cazul gestionarului GridLayout, suprafata de afi¸are este considerat˘ ca fiind ¸ s a un tabel ˆ a, spre deosebire de acesta, num˘rul de linii ¸i de coloane sunt ıns˘ a s determinate 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 at o component˘ poate ocupa mai multe celule adiacente, chiar de dimensiuni a diferite, zona ocupat˘ fiind referit˘ prin ”regiunea de afi¸are” a componentei a a s respective. Pentru a specifica modul de afi¸are a unei componente, acesteia ˆ este s ıi asociat un obiect de tip GridBagConstraints, ˆ care se specific˘ diferite ın a propriet˘¸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 as obiect 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 a tiv˘. Aceste constrˆngeri vor fi specificate prin intermediul unui obiect de tip a a GridBagConstraints, care poate fi refolosit pentru mai multe componente care 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 a urm˘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 ın simplifica codul, a fost creat˘ o metod˘ responsabil˘ cu setarea valorilor a a a gridx, gridy, gridwidth, gridheight ¸i ad˘ugarea unei componente cu s a restrictiile stabilite pe fereastr˘. ¸ a Listing 9.7: Gestionarul GridBagLayout import java . awt .*; public class TestGridBag Layout { static Frame f ; static GridBagLayout gridBag ; static GridBagConstr aint s gbc ; static void adauga ( Component comp , int x , int y , int w , int h ) { gbc . gridx = x ; gbc . gridy = y ; gbc . gridwidth = w ; gbc . gridheight = h ; gridBag . setConstraints ( comp , gbc ) ; f . add ( comp ) ; } public static void main ( String args []) { f = new Frame ( " Test GridBagLayout " ) ; gridBag = new GridBagLayout () ; gbc = new GridBa gC ons tr ai nt s () ; gbc . weightx = 1.0; gbc . weighty = 1.0;
  • 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 a gruparea componentelor ˆ ınrudite ca functii astfel ˆ at s˘ putem fi siguri c˘, ¸ ıncˆ a a indiferent 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 reprezentare vizibil˘, rolul s˘u fiind de a oferi o suprafat˘ de afi¸are pentru componente a a ¸a s grafice, inclusiv pentru alte panel-uri. Clasa care instantiaza aceste obiecte ¸ este Panel, extensie a superclasei Container. Pentru a aranja corespunz˘tor a componentele grupate ˆ ıntr-un panel, acestuia i se poate specifica un gestionar de 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 componentelor import java . awt .*; public class TestPanel { public static void main ( String args []) { Frame f = new Frame ( " Test Panel " ) ; Panel intro = new Panel () ; intro . setLayout ( new GridLayout (1 , 3) ) ; intro . add ( new Label ( " Text : " ) ) ; intro . add ( new TextField ( " " , 20) ) ; intro . add ( new Button ( " Adaugare " ) ) ; Panel lista = new Panel () ; lista . setLayout ( new FlowLayout () ) ; lista . add ( new List (10) ) ; lista . add ( new Button ( " Stergere " ) ) ;
  • 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 evenimentelor Un eveniment este produs de o actiune a utilizatorului asupra unei compo- ¸ nente grafice ¸i reprezint˘ mecanismul prin care utilizatorul comunic˘ efectiv s a a cu programul. Exemple de evenimente sunt: ap˘sarea unui buton, modi- a ficarea textului ˆ ıntr-un control de editare, ˆınchiderea sau redimensionarea unei ferestre, etc. Componentele care genereaz˘ anumite evenimente se mai a numesc ¸i surse de evenimente. s Interceptarea evenimentelor generate de componentele unui program se realizeaz˘ prin intermediul unor clase de tip listener (ascult˘tor, consumator a a de evenimente). In Java, orice obiect poate ”consuma” evenimentele generate de o anumit˘ component˘ grafic˘. a a a A¸adar, pentru a scrie cod care s˘ se execute ˆ momentul ˆ care utiliza- s a ın ın torul interactioneaz˘ cu o component˘ grafic˘ trebuie s˘ facem urm˘toarele a a a a a lucruri: • s˘ scriem o clas˘ de tip listener care s˘ ”asculte” evenimentele produse a a a de acea component˘ ¸i ˆ cadrul acestei clase s˘ 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 ın aceste obiecte se ˆımpart ˆ mai multe tipuri ˆ functie de componenta care ın ın ¸ le genereaz˘, mai precis ˆ functie de actiunea utilizatorului asupra acesteia. a ın ¸ ¸ Pentru fiecare tip de eveniment exist˘ o clas˘ care instantiaz˘ obiecte de a a ¸ a acel tip. De exemplu, evenimentul generat de actionarea unui buton este ¸ descris de clasa ActionEvent, cel generat de modificarea unui text de clasa TextEvent, 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 a specifica ˆ declaratia sa c˘ dore¸te s˘ asculte evenimente de un anumit ın ¸ a s a tip. Acest lucru se realizeaz˘ prin implementarea unei interfete specifice a ¸ fiec˘rui tip de eveniment. Astfel, pentru ascultarea evenimentelor de tip a ActionEvent clasa respectiv˘ trebuie s˘ implementeze interfata ActionListener, a a ¸ pentru TextEvent interfat˘ care trebuie implementata este TextListener, ¸a etc. Toate aceste interfete sunt derivate din EventListener. ¸ Fiecare interfat˘ define¸te una sau mai multe metode care vor fi apelate ¸a s automat la aparitia unui eveniment: ¸ class AscultaButoane implements ActionListener { public void actionPerformed(ActionEvent e) { // Metoda interfetei ActionListener ... } } class AscultaTexte implements TextListener { public void textValueChanged(TextEvent e) { // Metoda interfetei TextListener ... } } Intrucˆt o clas˘ poate implementa oricˆte interfete, ea va putea s˘ asculte a a a ¸ a evenimente de mai multe tipuri:
  • 222.
    9.4. TRATAREA EVENIMENTELOR 221 class 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 ¸ s s˘ implementeze o clas˘ consumatoare de evenimente. a a A¸a cum am spus mai devreme, pentru ca evenimentele unei componente s s˘ fie interceptate de c˘tre o instant˘ a unei clase ascult˘tor, aceast˘ clas˘ a a ¸a a a a trebuie ˆınregistrata ˆ lista ascult˘torilor componentei respective. Am spus ın a lista, deoarece evenimentele unei componente pot fi ascultate de oricˆte clase, a cu conditia ca acestea s˘ fie ˆ ¸ a ınregistrate la componenta respectiv˘. Inregis- a trarea unei clase ˆ lista ascult˘torilor unei componente se face cu metode ın a din clasa Component de tipul addTipEvenimentListener, iar eliminarea ei din 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 evenimentelor Inainte de a detalia aspectele prezentate mai sus, s˘ consider˘m un exemplu a a de tratare a evenimentelor. Vom crea o fereastr˘ care s˘ contin˘ dou˘ bu- a a ¸ a a toane cu numele ”OK”, repectiv ”Cancel”. La ap˘sarea fiec˘rui buton vom a a scrie pe bara de titlu a ferestrei mesajul ”Ati apasat butonul ...”. Listing 9.9: Ascultarea evenimentelor a dou˘ butoane a import java . awt .*; import java . awt . event .*;
  • 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- a mentelor. In exemplul de mai sus am definit clasa Ascultator pentru a intercepta evenimentele produse de cele dou˘ butoane ¸i din acest motiv a a s trebuit s˘ trimitem ca parametru constructorului clasei o referinta la fereas- a ¸ tra noastr˘. Mai simplu ar fi fost s˘ folosim chiar clasa Fereastra pentru a a a trata evenimentele produse de componentele sale. Vom modifica putin ¸i ¸ s
  • 224.
    9.4. TRATAREA EVENIMENTELOR 223 aplicatia pentru a pune ˆ evidenta o alt˘ modalitate de a determina com- ¸ ın ¸ a ponenta generatoare a unui eveniment - metoda getSource. Listing 9.10: Tratarea evenimentelor ˆ ferestr˘ ın a import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { Button ok = new Button ( " OK " ) ; Button exit = new Button ( " Exit " ) ; int n =0; public Fereastra ( String titlu ) { super ( titlu ) ; setLayout ( new FlowLayout () ) ; setSize (200 , 100) ; add ( ok ) ; add ( exit ) ; ok . addActionListener ( this ) ; exit . addActionListener ( this ) ; // Ambele butoane sunt ascultate in clasa Fereastra // deci ascultatorul este instanta curenta : this } // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { if ( e . getSource () == exit ) System . exit (0) ; // Terminam aplicatia if ( e . getSource () == ok ) { n ++; this . setTitle ( " Ati apasat OK de " + n + " ori " ) ; } } } public class TestEvent2 { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test Event " ) ; f . show () ; } }
  • 225.
    224 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ A¸adar, orice clas˘ poate asculta evenimente de orice tip cu conditia s˘ s a ¸ a implementeze interfetele specifice acelor tipuri de evenimente. ¸ 9.4.2 Tipuri de evenimente Evenimentele se ˆ ımpart ˆ dou˘ categorii: de nivel jos ¸i semantice. ın a s Evenimentele de nivel jos reprezint˘ o interactiune de nivel jos cum ar a ¸ 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 s operatiunile 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 pentru ap˘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 ¸ a GUI: ap˘sarea unui buton, selectarea unui articol dintr-o list˘, etc. Clasele a a care 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 s generate, prezentate sub forma interfetelor corespunz˘toare. Evident, eveni- ¸ a mentele generate de o superclas˘, cum ar fi Component, se vor reg˘si ¸i pentru a a s toate 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 a deoarece evenimentele legate de deplasarea mouse-ului sunt generate foarte frecvent ¸i receptionarea lor poate avea un impact negativ asupra vitezei de s ¸ executie, ˆ situatia cˆnd tratarea acestora nu ne intereseaz˘ ¸i dorim s˘ ¸ ın ¸ a a s a trat˘m doar evenimente de tip click, de exemplu. a Orice clas˘ care trateaz˘ evenimente trebuie s˘ implementeze obligatoriu a a a metodele interfetelor corespunz˘toare. Tabelul de mai jos prezint˘, pentru ¸ a a fiecare interfat˘, metodele puse la dispozitie ¸i care trebuie implementate de ¸a s c˘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 s cate de componente diferite, este necesar s˘ putem afla, ˆ cadrul uneia din a ın metodele de mai sus, care este sursa evenimentului pe care ˆ trat˘m pen- ıl a tru a putea reactiona ˆ consecint˘. Toate tipurile de evenimente mo¸tenesc ¸ ın ¸a s metoda getSource care returneaz˘ obiectul responsabil cu generarea eveni- a mentului. In cazul ˆ care dorim s˘ diferentiem doar tipul componentei surs˘, ın a ¸ a
  • 228.
    9.4. TRATAREA EVENIMENTELOR 227 putem folosi operatorul instanceof. public void actionPerformed(ActionEvent e) { Object sursa = e.getSource(); if (sursa instanceof Button) { // A fost apasat un buton Button btn = (Button) sursa; if (btn == ok) { // A fost apasat butonul ’ok’ } ... } if (sursa instanceof TextField) { // S-a apasat Enter dupa editarea textului TextField tf = (TextField) sursa; if (tf == nume) { // A fost editata componenta ’nume’ } ... } } 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 a particularit˘¸i vor fi prezentate mai detaliat ˆ sectiunile dedicate fiecˆrei at ın ¸ a componente ˆ parte. ın 9.4.3 Folosirea adaptorilor ¸i a claselor anonime s Am vazut c˘ o clas˘ care trateaz˘ evenimente de un anumit tip trebuie s˘ im- a a a a plementeze interfata corespunz˘toare acelui tip. Aceasta ˆ ¸ a ınseamn˘ c˘ trebuie a a s˘ implementeze obligatoriu toate metodele definite de acea interfat˘, chiar a ¸a dac˘ nu specific˘ nici un cod pentru unele dintre ele. Sunt ˆ a situatii cˆnd a a ıns˘ ¸ a acest lucru poate deveni sup˘rator, mai ales atunci cˆnd nu ne intereseaz˘ a a a decˆt o singura metod˘ a interfetei. a a ¸ Un exemplu sugestiv este urm˘torul: o fereastr˘ care nu are specificat cod a a pentru 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 a ferestrei ˆ metoda windoClosing ¸i apelat˘ metoda dispose de ˆ ın s a ınchidere a ferestrei, sau System.exit pentru terminarea programului, ˆ cazul cˆnd ın a este vorba de fereastra principal˘ a aplicatiei. Aceasta ˆ a ¸ ınseamn˘ c˘ trebuie a a s˘ implement˘m interfata WindowListener care are nu mai putin de ¸apte a a ¸ ¸ s metode. Listing 9.11: Implementarea interfetei WindowListener ¸ import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements WindowListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( this ) ; } // Metodele interfetei WindowListener public void windowOpened ( WindowEvent e ) {} public void windowClosing ( WindowEvent e ) { // Terminare program System . exit (0) ; } public void windowClosed ( WindowEvent e ) {} public void windowIconified ( WindowEvent e ) {} public void windowDeiconified ( WindowEvent e ) {} public void windowActivated ( WindowEvent e ) {} public void windowDeact ivated ( WindowEvent e ) {} } public class TestWind ow Lis te ne r { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test WindowListener " ) ; f . show () ; } } Observati c˘ trebuie s˘ implement˘m toate metodele interfetei, chiar dac˘ ¸ a a a ¸ a nu scriem nici un cod pentru unele dintre ele. Singura metod˘ care ne intere- a seaz˘ este windowClosing, ˆ care specific˘m ce trebuie f˘cut atunci cˆnd a ın a a a utilizatorul doreste s˘ ˆ a ınchid˘ fereastra. Pentru a evita scrierea inutil˘ a a a
  • 230.
    9.4. TRATAREA EVENIMENTELOR 229 acestor 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 aa numesc adaptori. Un adaptor este o clas˘ abstract˘ care implementeaz˘ o anumit˘ interfat˘ a a a a ¸a f˘r˘ a specifica cod nici unei metode a interfetei. aa ¸ Scopul unei astfel de clase este ca la crearea unui ”ascult˘tor” de eveni- a mente, ˆ loc s˘ implement˘ o anumit˘ interfat˘ ¸i implicit toate metodele ın a a a ¸a s sale, 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 a scriem o anumit˘ secvent˘ de cod). a ¸a De exemplu, adaptorul interfetei WindowListener este WindowAdapter ¸ iar folosirea acestuia este dat˘ ˆ exemplul de mai jos: a ın Listing 9.12: Extinderea clasei WindowAdapter import java . awt .*; import java . awt . event .*; class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new Ascultator () ) ; } } class Ascultator extends WindowAdapter { // Suprdefinim metodele care ne intereseaza public void windowClosing ( WindowEvent e ) { System . exit (0) ; } } public class TestWindowAdapter { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test WindowAdapter " ) ; f . show () ; } } Avantajul clar al acestei modalit˘¸i de tratare a evenimentelor este re- at ducerea codului programului, acesta devenind mult mai lizibil. Ins˘ exist˘ ¸i a as dou˘ 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 deja clasa Frame ¸i din acest motiv am construit o nou˘ clas˘ numit˘ Ascultator. s a a a Vom vedea ˆ a c˘ acest dezavantaj poate fi eliminat prin folosirea unei clase ıns˘ a anonime. Un alt dezavantaj este c˘ orice gre¸eal˘ de sintax˘ ˆ declararea unei metode a s a a ın a 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 a class Ascultator extends WindowAdapter { // In loc de windowClosing scriem WindowClosing // Nu supradefinim vreo metoda a clasei WindowAdapter // Nu da nici o eroare // Nu face nimic ! public void WindowClosing(WindowEvent e) { System.exit(0); } } In tabelul de mai jos sunt dati toti adaptorii interfetelor de tip ”listener” ¸ ¸ ¸ - se oberv˘ c˘ o interfat˘ XXXListener are un adaptor de tipul XXXAdapter. a a ¸a Interfetele care nu au un adaptor sunt cele care definesc o singur˘ metod˘ ¸i ¸ a as prin 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 ın clasele anonime sunt clase interne folosite pentru instantierea unui singur ¸ obiect de un anumit tip. Un exemplu tipic de folosire a lor este instantierea ¸
  • 232.
    9.4. TRATAREA EVENIMENTELOR 231 adaptorilor direct ˆ corpul unei clase care contine componente ale c˘ror ın ¸ a evenimente trebuie tratate. Listing 9.13: Folosirea adaptorilor ¸i a claselor anonime s import java . awt .*; import java . awt . event .*; class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (400 , 400) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { // Terminam aplicatia System . exit (0) ; } }) ; final Label label = new Label ( " " , Label . CENTER ) ; label . setBackground ( Color . yellow ) ; add ( label , BorderLayout . NORTH ) ; this . addMouseListener ( new MouseAdapter () { public void mouseClicked ( MouseEvent e ) { // Desenam un cerc la fiecare click de mouse label . setText ( " Click ... " ) ; Graphics g = Fereastra . this . getGraphics () ; g . setColor ( Color . blue ) ; int raza = ( int ) ( Math . random () * 50) ; g . fillOval ( e . getX () , e . getY () , raza , raza ) ; } }) ; this . ad dM ous eM oti on Li st e n e r ( new Mo us eM ot ion Ad ap te r () { public void mouseMoved ( MouseEvent e ) { // Desenam un punct la coordonatele mouse - ului Graphics g = Fereastra . this . getGraphics () ; g . drawOval ( e . getX () , e . getY () , 1 , 1) ; } }) ;
  • 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 ferestrelor Dup˘ cum am v˘zut suprafetele de afi¸are ale componentelor sunt extensii a a ¸ s ale clasei Container. O categorie aparte a acestor containere o reprezint˘ a ferestrele. Acestea sunt descrise de clase derivate din Window, cele mai utilizate fiind Frame ¸i Dialog. s O aplicatie Java cu intefat˘ grafic˘ va fi format˘ din una sau mai multe ¸ ¸a a a ferestre, una dintre ele fiind numit˘ fereastra principal˘. a a 9.5.1 Clasa Window Clasa Window este rar utilizat˘ ˆ mod direct deoarece permite doar crearea a ın unor ferestre care nu au chenar ¸i nici bar˘ de meniuri. Este util˘ atunci s a a cˆnd dorim s˘ afi¸am ferestre care nu interactioneaz˘ cu utilizatorul ci doar a a s ¸ a ofer˘ anumite informatii. a ¸ Metodele mai importante ale clasei Window, care sunt de altfel mo¸tenite s de toate subclasele sale, sunt date de mai jos: • show - face vizibil˘ fereastra. Implicit, o fereastr˘ nou creat˘ nu este a a a vizibil˘; a • hide - face fereastra invizibil˘ 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 a 9.5.2 Clasa Frame Este derivat˘ a clasei Window ¸i este folosit˘ pentru crearea de ferestre inde- a s a pendente ¸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 a numit˘ ¸i fereastra principal˘. as a Constructorii uzuali ai clasei Frame permit crearea unei ferestre cu sau f˘r˘ titlu, initial invizibil˘. Pentru ca o fereastr˘ s˘ devin˘ vizibil˘ se va aa ¸ a a a a a apela metoda show definit˘ ˆ superclasa Window. a ın import java.awt.*; public class TestFrame { public static void main(String args[]) { Frame f = new Frame("Titlul ferestrei"); f.show(); } } Crearea ferestrelor prin instantierea direct˘ a obiectelor de tip Frame este ¸ a mai putin folosit˘. De obicei, ferestrele unui program vor fi definite ˆ clase ¸ a ın separate care extind clasa Frame, ca ˆ exemplul de mai jos: ın import java.awt.*; class Fereastra extends Frame{ // Constructorul public Fereastra(String titlu) { super(titlu); ... }
  • 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 a nent˘ grafic˘ nu este ad˘ugat˘, suprafata de afi¸are a feretrei va fi determi- a a a a ¸ s nat˘ 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 a metoda 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 a exemplul de mai jos: Listing 9.14: Structura general˘ a unei ferestre a import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { // Constructorul public Fereastra ( String titlu ) { super ( titlu ) ; // Tratam evenimentul de inchidere a ferestrei this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { dispose () ; // inchidem fereastra // sau terminam aplicatia System . exit (0) ; } }) ;
  • 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 a specifice 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 Dialog Toate interfetele grafice ofer˘ un tip special de ferestre destinate prelu˘rii ¸ a a unor informatii sau a unor date de la utilizator. Acestea se numesc ferestre ¸ de dialog sau casete de dialog ¸i sunt implementate prin intermediul clasei s Dialog, subclas˘ direct˘ a clasei Window. a a Diferenta major˘ dintre ferestrele de dialog ¸i ferestrele de tip Frame ¸ a s const˘ ˆ faptul c˘ o fereastr˘ de dialog este dependent˘ de o alt˘ fereastra a ın a a a a (normal˘ sau tot fereastr˘ de dialog), numit˘ ¸i fereastra p˘rinte. Cu alte a a as a cuvinte, ferestrele de dialog nu au o existent˘ de sine st˘t˘toare. Cˆnd fer- ¸a aa a eastra p˘rinte este distrus˘ sunt distruse ¸i ferestrele sale de dialog, cˆnd este a a s a minimizat˘ ferestrele sale de dialog sunt f˘cute invizibile iar cˆnd este restau- a a a rat˘ acestea sunt aduse la starea ˆ care se g˘seau ˆ momentul minimiz˘rii a ın a ın a ferestrei 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˘ a structori care s˘ specifice ¸i ace¸ti parametri. Constructorii clasei Dialog a s s sunt: Dialog(Frame parinte) Dialog(Frame parinte, String titlu) Dialog(Frame parinte, String titlu, boolean modala) Dialog(Frame parinte, boolean modala) Dialog(Dialog parinte) Dialog(Dialog parinte, String titlu) Dialog(Dialog parinte, String titlu, boolean modala)
  • 238.
    9.5. FOLOSIREA FERESTRELOR 237 Parametrul ”p˘rinte” reprezint˘ referinta la fereastra p˘rinte, ”titlu” a a ¸ a reprezint˘ titlul ferestrei iar prin argumentul ”modal˘” specific˘m dac˘ fer- a a a a eastra de dialog creat˘ va fi modal˘ (true) sau nemodal˘ (false - valoarea a a a implicit˘). a Crearea unei ferestre de dialog este relativ simpla ¸i se realizeaz˘ prin s a derivarea clasei Dialog. Comunicarea dintre fereastra de dialog ¸i fereastra s sa p˘rinte, pentru ca aceasta din urm˘ s˘ poat˘ folosi datele introduse (sau a a a a optiunea specificata) ˆ caseta de dialog, se poate realiza folosind una din ¸ ın urm˘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 a unui ¸ir de caractere. Fereastra principal˘ a aplicatiei va fi p˘rintele casetei s a a de dialog, va primi ¸irul de caractere introdus ¸i ˆ va modifica titlul ca fiind s s ısi acesta. Deschiderea ferestrei de dialog se va face la ap˘sarea unui buton al a ferestrei 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 dialog import java . awt .*; import java . awt . event .*; // Fereastra principala class 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 dialog class FerDialog extends Dialog implements ActionListener { public String raspuns = null ; private TextField text ; private Button ok , cancel ; public FerDialog ( Frame parinte , String titlu , boolean modala ) { super ( parinte , titlu , modala ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { raspuns = null ; dispose () ; } }) ; text = new TextField ( " " , 30) ; add ( text , BorderLayout . CENTER ) ; Panel panel = new Panel () ; ok = new Button ( " OK " ) ;
  • 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 principala public class TestDialog { public static void main ( String args []) { FerPrinc f = new FerPrinc ( " Fereastra principala " ) ; f . show () ; } } 9.5.4 Clasa FileDialog Pachetul java.awt pune la dispozitie ¸i un tip de fereastr˘ de dialog folosit˘ s a a pentru selectarea unui nume de fi¸ier ˆ vederea ˆ arc˘rii sau salv˘rii unui s ın ınc˘ a a fi¸ier: clasa FileDialog, derivat˘ din Dialog. Instantele acestei clase au un s a ¸ comportament comun dialogurilor de acest tip de pe majoritatea platformelor de lucru, dar forma ˆ care vor fi afi¸ate este specific˘ platformei pe care ın s a ruleaz˘ 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 a titlul ferestrei iar prin argumentul ”mod” specific˘m dac˘ ˆ arc˘m sau a a ınc˘ a salv˘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 fisier new FileDialog(parinte, "Alegere fisier", FileDialog.LOAD); // Dialog pentru salvarea unui fisier new FileDialog(parinte, "Salvare fisier", FileDialog.SAVE); La crearea unui obiect FileDialog acesta nu este implicit vizibil. Dac˘ a afi¸area sa se face cu show, caseta de dialog va fi modal˘. Dac˘ afi¸area s a a s se face cu setVisible(true), atunci va fi nemodal˘. Dup˘ selectarea unui a a fi¸ier ea va fi facut˘ automat invizibil˘. s a a Pe lˆng˘ metodele mo¸tenite de la superclasa Dialog clasa FileDialog a a s mai contine metode pentru obtinerea numelui fi¸ierului sau directorului se- ¸ ¸ s lectat getFile, getDirectory, pentru stabilirea unui criteriu de filtrare setFilenameFilter, etc. S˘ consider˘m un exemplu ˆ care vom alege, prin intermediul unui obiect a a ın FileDialog, un fi¸ier cu extensia ”java”. Directorul initial este directorul s ¸ curent, iar numele implicit este TestFileDialog.java. Numele fi¸ieruluis ales va fi afi¸at la consol˘. s a Listing 9.16: Folosirea unei ferestre de dialog import java . awt .*; import java . awt . event .*; import java . io .*; class FerPrinc extends Frame implements ActionListener { public FerPrinc ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () {
  • 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 a JFileChooser care ofer˘ mai multe facilit˘¸i ¸i prin urmare va constitui a at s prima optiune ˆ ¸ ıntr-o aplicatie cu intefat˘ grafic˘. ¸ ¸a a
  • 243.
    242 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ 9.6 Folosirea meniurilor Spre deosebire de celelalte obiecte grafice care deriv˘ din clasa Component, a componentele unui meniu reprezint˘ instante ale unor clase derivate din su- a ¸ perclasa abstract˘ MenuComponent. Aceast˘ exceptie este facut˘ deoarece a a ¸ a unele 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 a principale File ¸i Edit. Meniul Edit contine la rˆndul lui alt meniu (submeniu) s ¸ a Options, articolul Undo ¸i dou˘ comutatoare Bold ¸i Italic. Prin abuz de s a s limbaj, vom referi uneori bara de meniuri a unei ferestre ca fiind meniul ferestrei. In modelul AWT obiectele care reprezint˘ bare de meniuri sunt reprezen- a tate ca instante al clasei MenuBar. Un obiect de tip MenuBar contine obiecte ¸ ¸ de tip Menu, care sunt de fapt meniurile derulante propriu-zise. La rˆndula lor, 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 a interfata MenuContainer. Cel mai adesea, meniurile sunt ata¸ate fere- ¸ s strelor, mai precis obiectelor de tip Frame, acestea implementˆnd interfat˘ a ¸a MenuContainer. Ata¸area unei bare de meniuri la o fereastr˘ se face prin s a metoda addMenuBar a clasei Frame. 9.6.1 Ierarhia claselor ce descriu meniuri S˘ vedem ˆ continuare care este ierarhia claselor folosite ˆ lucrul cu meniuri a ın ın ¸i s˘ analiz˘m pe rˆnd aceste clase: s a a a Clasa MenuComponent este o clasa abstract˘ din care sunt extinse a toate celelalte clase folosite la crearea de meniuri, fiind similar˘ celeilalte a superclase 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 ferestre cadru de tip Frame, adaptˆnd conceptul de bar˘ de meniuri la platforma a a curent˘ de lucru. Dup˘ cum am mai spus, pentru a lega bara de meniuri la a a o anumit˘ fereastra trebuie apelat˘ metoda setMenuBar din clasa Frame. a a // Crearea barei de meniuri MenuBar mb = new MenuBar();
  • 245.
    244 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ // Adaugarea meniurilor derulante la bara de meniuri ... // Atasarea barei de meniuri la o fereastra Frame f = new Frame("Fereastra cu meniu"); f.addMenuBar(mb); Orice articol al unui meniu trebuie s˘ 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 ¸a clasei MenuItem reprezint˘ de fapt un buton sau un comutator, cu o anumit˘ a a etichet˘ care va ap˘rea ˆ meniu, ˆ ¸it˘ eventual de un accelerator (obiect a a ın ınsot a de tip MenuShortcut) ce reprezint˘ combinatia de taste cu care articolul a ¸ poate fi apelat rapid (vezi ”Acceleratori”). Clasa Menu permite crearea unui meniu derulant ˆ ıntr-o bar˘ de meniuri. a Optional, un meniu poate fi declarat ca fiind tear-off, ceea ce ˆ ¸ ınseamn˘ c˘ a a poate fi deschis ¸i deplasat cu mouse-ul (dragged) ˆ s ıntr-o alt˘ pozitie decˆt a ¸ a cea 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 a care este de fapt numele s˘u ce va fi afi¸at pe bara de meniuri. Articolele a s dintr-un meniu trebuie s˘ apartin˘ clasei MenuItem, ceea ce ˆ a ¸ a ınseamn˘ c˘ pot a a fi instante ale uneia din clasele MenuItem, Menu sau CheckboxMenuItem. ¸ Clasa CheckboxMenuItem implementeaz˘ ˆ a ıntr-un meniu articole de tip 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 ın comutator ˆ dreptul etichetei sale va fi afi¸at un simbol grafic care indic˘ ın s a acest lucru; la invalidarea sa, simbolul grafic respectiv va disp˘rea. Clasa a CheckboxMenuItem 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 s meniu ca ˆ figura prezentat˘ anterior: ın a Listing 9.17: Crearea unui meniu import java . awt .*; import java . awt . event .*; public class TestMenu { public static void main ( String args []) { Frame f = new Frame ( " Test Menu " ) ; MenuBar mb = new MenuBar () ; Menu fisier = new Menu ( " File " ) ; fisier . add ( new MenuItem ( " Open " ) ) ; fisier . add ( new MenuItem ( " Close " ) ) ; fisier . addSeparator () ; fisier . add ( new MenuItem ( " Exit " ) ) ; Menu optiuni = new Menu ( " Options " ) ; optiuni . add ( new MenuItem ( " Copy " ) ) ; optiuni . add ( new MenuItem ( " Cut " ) ) ; optiuni . add ( new MenuItem ( " Paste " ) ) ; Menu editare = new Menu ( " Edit " ) ; editare . add ( new MenuItem ( " Undo " ) ) ; editare . add ( optiuni ) ; editare . addSeparator () ; editare . add ( new CheckboxMenuItem ( " Bold " ) ) ; editare . add ( new CheckboxMenuItem ( " Italic " ) ) ; mb . add ( fisier ) ; mb . add ( editare ) ; f . setMenuBar ( mb ) ; f . setSize (200 , 100) ; f . show () ; } }
  • 247.
    246 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ 9.6.2 Tratarea evenimentelor generate de meniuri La alegerea unei optiuni dintr-un meniu se genereaz˘ fie un eveniment de tip ¸ a ActionEvent dac˘ articolul respectiv este de tip MenuItem, fie ItemEvent a pentru comutatoarele CheckboxMenuItem. A¸adar, pentru a activa optiunile s ¸ unui meniu trebuie implementate interfatele ActionListener sau/¸i Item- ¸ s Listener ˆ cadrul obiectelor care trebuie s˘ specifice codul ce va fi executat ın a la alegerea unei optiuni ¸i implementate metodele actionPerformed, respec- ¸ s tiv itemStatChanged. Fiec˘rui meniu ˆ putem asocia un obiect receptor a ıi diferit, ceea ce u¸ureaz˘ munca ˆ cazul ˆ care ierarhia de meniuri este com- s a ın ın plex˘. Pentru a realiza leg˘tura ˆ a a ıntre obiectul meniu ¸i obiectul de tip listener s trebuie s˘ adaug˘m receptorul ˆ lista de ascult˘tori a meniului respectiv, a a ın a ˆ ıntocmai ca pe orice component˘, folosind metodele addActionListener, a respectiv addItemListener. A¸adar, tratarea evenimentelor generate de obiecte de tip MenuItem este s identic˘ cu tratarea butoanelor, ceea ce face posibil ca unui buton de pe a suprafata de 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 a alegerii optiunii, f˘cˆndu-se o singur˘ dat˘ ˆ ¸ a a a a ıntr-o clas˘ care este ˆ a ınregistrat˘ a ca 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˘ a cu List, Choice, CheckBox, toate implementˆnd interfata ItemSelectable a ¸ ¸i deci tratarea lor va fi f˘cut˘ la fel. Tipul de operatie selectare / deselectare s a a este codificat ˆ evenimentul generat de cˆmpurile statice ItemEvent.SELECTED ın a ¸i ItemEvent.DESELECTED. s Listing 9.18: Tratarea evenimentelor unui meniu import java . awt .*; import java . awt . event .*; public class TestMenuEvent extends Frame implements ActionListener , ItemListener { public TestMenuEvent ( String titlu ) { super ( titlu ) ; MenuBar mb = new MenuBar () ; Menu test = new Menu ( " Test " ) ; CheckboxMenuItem check = new CheckboxMenuItem ( " Check me " ) ;
  • 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 s clasei PopupMenu, subclas˘ direct˘ a clasei Menu. Sunt meniuri invizibile a a care 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 aa s˘u drept. Metodele de ad˘ugare a articolelor unui meniu de context sunt a a mo¸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 avea s a acces rapid la meniu. Argumentul ”origine” reprezint˘ componenta fat˘ de a ¸a originile c˘reia se va calcula pozitia de afi¸are a meniului popup. De obicei, a ¸ s reprezint˘ 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 a determin˘ dac˘ un eveniment de tip MouseEvent poate fi responsabil cu a a deschiderea unui meniu de context. Aceasta este isPopupTrigger ¸i este s definit˘ ˆ 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 a ci se ata¸eaz˘ la o component˘ (de obicei la o fereastr˘) prin metoda add s a a a a acesteia. In cazul cˆnd avem mai multe meniuri popup pe care vrem s˘ a a le folosim ˆıntr-o fereastr˘, trebuie s˘ le definim pe toate ¸i, la un moment a a s dat, vom ad˘uga ferestrei meniul corespunz˘tor dup˘ care ˆ vom face vizibil. a a a ıl Dup˘ ˆ a ınchiderea acestuia, vom ”rupe” leg˘tura ˆ a ıntre fereastr˘ ¸i meniu prin as instructiunea remove: ¸ fereastra.add(popup1); ... fereastra.remove(popup1); fereastra.add(popup2); In exemplul de mai jos, vom crea un meniu de contex pe care ˆ vom activa ıl la ap˘sarea butonului drept al mouse-ului pe suprafata ferestrei principale. a ¸ Tratarea evenimentelor generate de un meniu popup se realizeaz˘ identic ca a pentru 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 a intermediul tastaturii, a optiunilor dintr-un meniu. Astfel, oric˘rui obiect ¸ a de tip MenuItem ˆ poate fi asociat un obiect de tip accelerator, definit prin ıi intermediul clasei MenuShortcut. Singurele combinatii de taste care pot ¸ juca rolul acceleratorilor sunt: Ctrl + Tasta sau Ctrl + Shift + Tasta. Atribuirea unui accelerator la un articol al unui meniu poate fi realizat˘ prin a constructorul obiectelor de tip MenuItem ˆ forma: ın MenuItem(String eticheta, MenuShortcut accelerator), ca ˆ exempleleın de mai jos: // Ctrl+O new MenuItem("Open", new MenuShortcut(KeyEvent.VK_O)); // Ctrl+P new MenuItem("Print", new MenuShortcut(’p’)); // Ctrl+Shift+P new MenuItem("Preview", new MenuShortcut(’p’), true); 9.7 Folosirea componentelor AWT In continuare vor fi date exemple de folosire ale componentelor AWT, ˆ care ın s˘ 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 COMPONENTELORAWT 251 9.7.1 Clasa Label Un obiect de tip Label (etichet˘) reprezint˘ o component˘ pentru plasarea a a a unui text pe o suprafata de afi¸are. O etichet˘ este format˘ dintr-o singur˘ ¸ s a a a linie de text static ce nu poate fi modificat de c˘tre utilizator, dar poate fi a modificat din program. Listing 9.20: Folosirea clasei Label import java . awt .*; public class TestLabel { public static void main ( String args []) { Frame f = new Frame ( " Label " ) ; Label nord , sud , est , vest , centru ; nord = new Label ( " Nord " , Label . CENTER ) ; nord . setForeground ( Color . blue ) ; sud = new Label ( " Sud " , Label . CENTER ) ; sud . setForeground ( Color . red ) ; vest = new Label ( " Vest " , Label . LEFT ) ; vest . setFont ( new Font ( " Dialog " , Font . ITALIC , 14) ) ; est = new Label ( " Est " , Label . RIGHT ) ; est . setFont ( new Font ( " Dialog " , Font . ITALIC , 14) ) ; centru = new Label ( " Centru " , Label . CENTER ) ; centru . setBackground ( Color . yellow ) ; centru . setFont ( new Font ( " Arial " , Font . BOLD , 20) ) ; f . add ( nord , BorderLayout . NORTH ) ; f . add ( sud , BorderLayout . SOUTH ) ; f . add ( est , BorderLayout . EAST ) ; f . add ( vest , BorderLayout . WEST ) ;
  • 253.
    252 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ f . add ( centru , BorderLayout . CENTER ) ; f . pack () ; f . show () ; } } 9.7.2 Clasa Button Un obiect de tip Button reprezint˘ o component˘ pentru plasarea unui bu- a a ton etichetat pe o suprafata de afi¸are. Textul etichetei este format dintr-o ¸ s singur˘ linie. a Listing 9.21: Folosirea clasei Button import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ActionListener { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( null ) ; setSize (200 , 120) ; Button b1 = new Button ( " OK " ) ; b1 . setBounds (30 , 30 , 50 , 70) ; b1 . setFont ( new Font ( " Arial " , Font . BOLD , 14) ) ;
  • 254.
    9.7. FOLOSIREA COMPONENTELORAWT 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 Checkbox Un obiect de tip Checkbox (comutator) reprezint˘ o component˘ care se a a poate 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 ın tar˘ 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 Checkbox import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label1 , label2 ; private Checkbox cbx1 , cbx2 , cbx3 ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (5 , 1) ) ; label1 = new Label ( " Ingrediente Pizza : " , Label . CENTER ) ; label1 . setBackground ( Color . orange ) ; label2 = new Label ( " " ) ; label2 . setBackground ( Color . lightGray ) ; cbx1 = new Checkbox ( " cascaval " ) ; cbx2 = new Checkbox ( " sunca " ) ; cbx3 = new Checkbox ( " ardei " ) ; add ( label1 ) ; add ( label2 ) ; add ( cbx1 ) ; add ( cbx2 ) ; add ( cbx3 ) ;
  • 256.
    9.7. FOLOSIREA COMPONENTELORAWT 255 setSize (200 , 200) ; cbx1 . addItemListener ( this ) ; cbx2 . addItemListener ( this ) ; cbx3 . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { StringBuffer ingrediente = new StringBuffer () ; if ( cbx1 . getState () == true ) ingrediente . append ( " cascaval " ) ; if ( cbx2 . getState () == true ) ingrediente . append ( " sunca " ) ; if ( cbx3 . getState () == true ) ingrediente . append ( " ardei " ) ; label2 . setText ( ingrediente . toString () ) ; } } public class TestCheckbox { public static void main ( String args []) { Fereastra f = new Fereastra ( " Checkbox " ) ; f . show () ; } } 9.7.4 Clasa CheckboxGroup Un obiect de tip CheckboxGroup define¸te un grup de comutatoare din care s doar unul poate fi selectat. Uzual, aceste componente se mai numesc bu- toane radio. Aceast˘ clas˘ nu este derivat˘ din Component, oferind doar o a a a modalitate de grupare a componentelor de tip Checkbox.
  • 257.
    256 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Listing 9.23: Folosirea clasei CheckboxGroup import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label1 , label2 ; private Checkbox cbx1 , cbx2 , cbx3 ; private CheckboxGroup cbg ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (5 , 1) ) ; label1 = new Label ( " Alegeti postul TV " , Label . CENTER ) ; label1 . setBackground ( Color . orange ) ; label2 = new Label ( " " , Label . CENTER ) ; label2 . setBackground ( Color . lightGray ) ; cbg = new CheckboxGroup () ; cbx1 = new Checkbox ( " HBO " , cbg , false ) ; cbx2 = new Checkbox ( " Discovery " , cbg , false ) ; cbx3 = new Checkbox ( " MTV " , cbg , false ) ; add ( label1 ) ; add ( label2 ) ; add ( cbx1 ) ;
  • 258.
    9.7. FOLOSIREA COMPONENTELORAWT 257 add ( cbx2 ) ; add ( cbx3 ) ; setSize (200 , 200) ; cbx1 . addItemListener ( this ) ; cbx2 . addItemListener ( this ) ; cbx3 . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { Checkbox cbx = cbg . getS e lec ted Che ck box () ; if ( cbx != null ) label2 . setText ( cbx . getLabel () ) ; } } public class TestCheckboxGroup { public static void main ( String args []) { Fereastra f = new Fereastra ( " CheckboxGroup " ) ; f . show () ; } } 9.7.5 Clasa Choice Un obiect de tip Choice define¸te o list˘ de optiuni din care utilizatorul s a ¸ poate selecta una singur˘. La un moment dat, din ˆ a ıntreaga list˘ doar o sin- a gur˘ optiune este vizibil˘, cea selectat˘ ˆ momentul curent. O component˘ a ¸ a a ın a Choice este ˆ ¸it˘ de un buton etichetat cu o sageat˘ vertical˘ la ap˘sarea ınsot a a a a c˘ruia este afi¸at˘ ˆ a s a ıntreaga sa list˘ de elemente, pentru ca utilizatorul s˘ a a poat˘ selecta o anumit˘ optiune. a a ¸
  • 259.
    258 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Listing 9.24: Folosirea clasei Choice import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label ; private Choice culori ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (4 , 1) ) ; label = new Label ( " Alegeti culoarea " ) ; label . setBackground ( Color . red ) ; culori = new Choice () ; culori . add ( " Rosu " ) ; culori . add ( " Verde " ) ; culori . add ( " Albastru " ) ; culori . select ( " Rosu " ) ; add ( label ) ; add ( culori ) ; setSize (200 , 100) ; culori . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { switch ( culori . getSelectedIndex () ) { case 0: label . setBackground ( Color . red ) ; break ; case 1: label . setBackground ( Color . green ) ; break ; case 2: label . setBackground ( Color . blue ) ;
  • 260.
    9.7. FOLOSIREA COMPONENTELORAWT 259 } } } public class TestChoice { public static void main ( String args []) { Fereastra f = new Fereastra ( " Choice " ) ; f . show () ; } } 9.7.6 Clasa List Un obiect de tip List define¸te o list˘ de optiuni care poate fi setat˘ astfel s a ¸ a ˆ at utilizatorul s˘ poat˘ selecta o singur˘ optiune sau mai multe. Toate ıncˆ a a a ¸ elementele listei sunt vizibile ˆ limita dimensiunilor grafice ale componentei. ın Listing 9.25: Folosirea clasei List import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements ItemListener { private Label label ; private List culori ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () {
  • 261.
    260 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (2 , 1) ) ; label = new Label ( " Alegeti culoarea " , Label . CENTER ) ; label . setBackground ( Color . red ) ; culori = new List (3) ; culori . add ( " Rosu " ) ; culori . add ( " Verde " ) ; culori . add ( " Albastru " ) ; culori . select (3) ; add ( label ) ; add ( culori ) ; setSize (200 , 200) ; culori . addItemListener ( this ) ; } // Metoda interfetei ItemListener public void itemStateChanged ( ItemEvent e ) { switch ( culori . getSelectedIndex () ) { case 0: label . setBackground ( Color . red ) ; break ; case 1: label . setBackground ( Color . green ) ; break ; case 2: label . setBackground ( Color . blue ) ; } } } public class TestList { public static void main ( String args []) { Fereastra f = new Fereastra ( " List " ) ; f . show () ; } }
  • 262.
    9.7. FOLOSIREA COMPONENTELORAWT 261 9.7.7 Clasa ScrollBar Un obiect de tip Scrollbar define¸te o bar˘ de defilare pe vertical˘ sau s a a orizontal˘. Este util˘ pentru punerea la dispozitia utilizatorului a unei a a ¸ modalit˘¸i sugestive de a alege o anumit˘ valoare dintr-un interval. at a Listing 9.26: Folosirea clasei ScrollBar import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements Ad ju st me nt Lis te ne r { private Scrollbar scroll ; private Label valoare ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (2 , 1) ) ; valoare = new Label ( " " , Label . CENTER ) ; valoare . setBackground ( Color . lightGray ) ; scroll = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 101) ; add ( valoare ) ; add ( scroll ) ; setSize (200 , 80) ; scroll . add Adjus tmen tLis t e n e r ( this ) ; } // Metoda interfetei Adju st me ntL is te ne r
  • 263.
    262 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ public void ad ju st m e n t V a lu eC ha n g e d ( AdjustmentEvent e ) { valoare . setText ( scroll . getValue () + " % " ) ; } } public class TestScrollbar { public static void main ( String args []) { Fereastra f = new Fereastra ( " Scrollbar " ) ; f . show () ; } } 9.7.8 Clasa ScrollPane Un obiect de tip ScrollPane permite ata¸area unor bare de defilare (ori- s zontal˘ ¸si/sau vertical˘) oric˘rei componente grafice. Acest lucru este util as a a pentru acele componente care nu au implementat˘ functionalitatea de defi- a ¸ lare automat˘, cum ar fi listele (obiecte din clasa List). a Listing 9.27: Folosirea clasei ScrollPane import java . awt .*; import java . awt . event .*; class Fereastra extends Frame { private ScrollPane sp ; private List list ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ;
  • 264.
    9.7. FOLOSIREA COMPONENTELORAWT 263 } }) ; list = new List (7) ; list . add ( " Luni " ) ; list . add ( " Marti " ) ; list . add ( " Miercuri " ) ; list . add ( " Joi " ) ; list . add ( " Vineri " ) ; list . add ( " Sambata " ) ; list . add ( " Duminica " ) ; list . select (1) ; sp = new ScrollPane ( ScrollPane . SC ROLLBAR S_ALWAY S ) ; sp . add ( list ) ; add ( sp , BorderLayout . CENTER ) ; setSize (200 , 200) ; } } public class TestScrollPane { public static void main ( String args []) { Fereastra f = new Fereastra ( " ScrollPane " ) ; f . show () ; } } 9.7.9 Clasa TextField Un obiect de tip TextField define¸te un control de editare a textului pe o s singur˘ linie. Este util pentru interogarea utilizatorului asupra unor valori. a
  • 265.
    264 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ Listing 9.28: Folosirea clasei TextField import java . awt .*; import java . awt . event .*; class Fereastra extends Frame implements TextListener { private TextField nume , parola ; private Label acces ; private static final String UID = " Duke " , PWD = " java " ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setLayout ( new GridLayout (3 , 1) ) ; setBackground ( Color . lightGray ) ; nume = new TextField ( " " , 30) ; parola = new TextField ( " " , 10) ; parola . setEchoChar ( ’* ’) ; Panel p1 = new Panel () ; p1 . setLayout ( new FlowLayout ( FlowLayout . LEFT ) ) ; p1 . add ( new Label ( " Nume : " ) ) ; p1 . add ( nume ) ; Panel p2 = new Panel () ; p2 . setLayout ( new FlowLayout ( FlowLayout . LEFT ) ) ; p2 . add ( new Label ( " Parola : " ) ) ; p2 . add ( parola ) ; acces = new Label ( " Introduceti numele si parola ! " , Label . CENTER ) ; add ( p1 ) ; add ( p2 ) ; add ( acces ) ; setSize (350 , 100) ; nume . addTextListener ( this ) ; parola . addTextListener ( this ) ; }
  • 266.
    9.7. FOLOSIREA COMPONENTELORAWT 265 // Metoda interfetei TextListener public void textValueChanged ( TextEvent e ) { if ( nume . getText () . length () == 0 || parola . getText () . length () == 0) { acces . setText ( " " ) ; return ; } if ( nume . getText () . equals ( UID ) && parola . getText () . equals ( PWD ) ) acces . setText ( " Acces permis ! " ) ; else acces . setText ( " Acces interzis ! " ) ; } } public class TestTextField { public static void main ( String args []) { Fereastra f = new Fereastra ( " TextField " ) ; f . show () ; } } 9.7.10 Clasa TextArea Un obiect de tip TextArea define¸te un control de editare a textului pe mai s multe linii. Este util pentru editarea de texte, introducerea unor comentarii, etc . Listing 9.29: Folosirea clasei TextArea import java . awt .*;
  • 267.
    266 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸ import java . awt . event .*; import java . io .*; class Fereastra extends Frame implements TextListener , ActionListener { private TextArea text ; private TextField nume ; private Button salvare ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowList ener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; setBackground ( Color . lightGray ) ; text = new TextArea ( " " , 30 , 10 , TextArea . S C RO L L BA R S _ VE R T I C A L _ O N L Y ) ; nume = new TextField ( " " , 12) ; salvare = new Button ( " Salveaza text " ) ; salvare . setEnabled ( false ) ; Panel fisier = new Panel () ; fisier . add ( new Label ( " Fisier : " ) ) ; fisier . add ( nume ) ; add ( fisier , BorderLayout . NORTH ) ; add ( text , BorderLayout . CENTER ) ; add ( salvare , BorderLayout . SOUTH ) ; setSize (300 , 200) ; text . addTextListener ( this ) ; salvare . addActionLi stener ( this ) ; } // Metoda interfetei TextListener public void textValueChanged ( TextEvent e ) { if ( text . getText () . length () == 0 || nume . getText () . length () == 0) salvare . setEnabled ( false ) ; else salvare . setEnabled ( true ) ;
  • 268.
    9.7. FOLOSIREA COMPONENTELORAWT 267 } // Metoda interfetei ActionListener public void actionPerformed ( ActionEvent e ) { String continut = text . getText () ; try { PrintWriter out = new PrintWriter ( new FileWriter ( nume . getText () ) ) ; out . print ( continut ) ; out . close () ; text . requestFocus () ; } catch ( IOException ex ) { ex . printStackTrace () ; } } } public class TestTextArea { public static void main ( String args []) { Fereastra f = new Fereastra ( " TextArea " ) ; f . show () ; } }
  • 269.
    268 ˘ CAPITOLUL 9. INTERFATA GRAFICA CU UTILIZATORUL ¸
  • 270.
    Capitolul 10 Desenarea 10.1 Conceptul de desenare Un program Java care are interfat˘ grafic˘ cu utilizatorul trebuie s˘ deseneze ¸a a a pe ecran toate componentele sale care au o reprezentare vizual˘. Aceast˘ a a desenare include componentele standard folosite ˆ aplicatie precum ¸i cele ın ¸ s definite de c˘tre programator. Desenarea componentelor se face automat ¸i a s este un proces care se execut˘ ˆ urm˘toarele situatii: a ın a ¸ • la afi¸area pentru prima dat˘ a unei componente; s a • la operatii de minimizare, maximizare, redimensionare a suprafetei de ¸ ¸ afi¸are; s • ca r˘spuns al unei solicit˘ri explicite a programului. a a Metodele care controleaz˘ procesul de desenare se g˘sesc ˆ clasa Component a a ın ¸i sunt urm˘toarele: s a • void paint(Graphics g) - Deseneaz˘ o component˘. Este o metod˘ a a a supradefinit˘ de fiecare component˘ ˆ parte pentru a furniza reprezentarea a a ın sa grafic˘ specific˘. Metoda este apelat˘ de fiecare dat˘ cˆnd continutul a a a a a ¸ componentei trebuie desenat sau redesenat ¸i nu va fi apelat˘ explicit. s a • void update(Graphics g) - Actualizeaz˘ starea grafic˘ a unei com- a a ponente. Actiunea acestei metode se realizeaz˘ ˆ trei pa¸i: ¸ a ın s 1. ¸terge componenta prin supradesenarea ei cu culoarea fundalului; s 269
  • 271.
    270 CAPITOLUL 10. DESENAREA 2. stabile¸te culoarea (foreground) a componentei; s 3. apeleaz˘ metoda paint pentru a redesena componenta. a • void repaint() - Execut˘ explicit un apel al metodei update pentru a a actualiza reprezentarea grafic˘ a unei componente. a Dup˘ cum se observ˘, singurul argument al metodelor paint ¸i update a a s este un obiect de tip Graphics. Acesta reprezint˘ contextul grafic ˆ care se a ın execut˘ desenarea componentelor (vezi ”Contextul grafic de desenare - clasa a Graphics”). Atentie ¸ Toate desenele care trebuie s˘ apar˘ pe o suprafata de desenare se real- a a ¸ izeaz˘ ˆ metoda paint a unei componente, ˆ general apelat˘ automat sau a ın ın a explicit cu metoda repaint ori de cˆte ori componenta respectiv˘ trebuie a a redesenat˘. Exist˘ posibilitatea de a desena ¸i ˆ afara metodei paint, ˆ a a a s ın ıns˘ aceste desene se vor pierde la prima operatie de minimizare, maximizare, ¸ redimensionare a suprafetei de afi¸are. ¸ s 10.1.1 Metoda paint Dup˘ cum am spus, toate desenele care trebuie s˘ apar˘ pe o suprafat˘ de a a a ¸a afi¸are se realizeaz˘ ˆ metoda paint a unei componente. Metoda paint este s a ın definit˘ ˆ superclasa Component ˆ a nu are nici o implementare ¸i, din acest a ın ıns˘ s motiv, orice obiect grafic care dore¸te s˘ se deseneze trebuie s˘ o suprade- s a a fineasc˘ pentru a-¸i crea propria sa reprezentare. Componentele standard a s AWT au deja supradefinit˘ aceast˘ metod˘ deci nu trebuie s˘ ne preocupe a a a a desenarea lor, ˆ a putem modifica reprezentarea lor grafic˘ prin crearea ıns˘ a unei subclase ¸i supradefinirea metodei paint, avˆnd ˆ a grij˘ s˘ apel˘m ¸i s a ıns˘ a a a s metoda superclasei care se ocup˘ cu desenarea efectiv˘ a componentei. a a In exemplul de mai jos, redefinim metoda paint pentru un obiect de tip Frame, pentru a crea o clas˘ ce instantiaz˘ ferestre pentru o aplicatie a ¸ a ¸ demonstrativ˘ (ˆ coltul stˆnga sus este afi¸at textul ”Aplicatie DEMO”). a ın ¸ a s
  • 272.
    10.1. CONCEPTUL DEDESENARE 271 Listing 10.1: Supradefinirea metodei paint import java . awt .*; class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (200 , 100) ; } public void paint ( Graphics g ) { // Apelam metoda paint a clasei Frame super . paint ( g ) ; g . setFont ( new Font ( " Arial " , Font . BOLD , 11) ) ; g . setColor ( Color . red ) ; g . drawString ( " Aplicatie DEMO " , 5 , 35) ; } } public class TestPaint { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test paint " ) ; f . show () ; } } Observati c˘ la orice redimensionare a ferestrei textul ”Aplicatie DEMO” ¸ a va fi redesenat. Dac˘ desenarea acestui text ar fi fost facut˘ oriunde ˆ alt˘ a a ın a parte decˆt ˆ metoda paint, la prima redimensionare a ferestrei acesta s-ar a ın pierde. A¸adar, desenarea ˆ Java trebuie s˘ se fac˘ doar ˆ cadrul metodelor s ın a a ın paint ale componentelor grafice. 10.1.2 Suprafete de desenare - clasa Canvas ¸ In afara posibilit˘¸ii de a utiliza componente grafice standard, Java ofer˘ at a ¸i posibilitatea controlului la nivel de punct (pixel) pe dispozitivul grafic, s respectiv desenarea a diferite forme grafice direct pe suprafata unei compo- ¸ nente. De¸i este posibil, ˆ general nu se deseneaz˘ la nivel de pixel direct s ın a pe suprafata ferestrelor sau a altor containere, ci vor fi folosite clase dedicate ¸ acestui scop. In AWT a fost definit un tip special de component˘ numit˘ Canvas a a (pˆnz˘ de pictor), al c˘rei scop este de a fi extins˘ pentru a implementa a a a a
  • 273.
    272 CAPITOLUL 10. DESENAREA obiecte grafice cu o anumit˘ ˆ a¸i¸are. A¸adar, Canvas este o clas˘ generic˘ a ınf˘t s s a a din care se deriveaz˘ subclase pentru crearea suprafetelor de desenare (plan¸e). a ¸ s Plan¸ele nu pot contine alte componente grafice, ele fiind utilizate doar ca s ¸ suprafete de desenat sau ca fundal pentru animatie. Desenarea pe o plan¸a ¸ ¸ s se face prin supradefinirea metodei paint a acesteia. Concret, o plan¸˘ este o suprafat˘ dreptunghiular˘ de culoare alb˘, pe sa ¸a a a care se poate desena. Dimensiunile sale implicite sunt 0 ¸i, din acest mo- s tiv, este recomandat ca o plan¸a s˘ redefineasca metoda getPreferredSize, s a eventual ¸i getMinimumSize, getMaximumSize, deoarece acestea vor fi apelate s de c˘tre gestionarii de pozitionare. a ¸ Etapele uzuale care trebuie parcurse pentru crearea unui desen, sau mai bine zis a unei componente cu o anumit˘ ˆ a¸i¸are, sunt: a ınf˘t s • crearea unei plan¸e de desenare, adic˘ o subclas˘ a lui Canvas; s a a • redefinirea metodei paint din clasa respectiv˘; a • redefinirea metodelor getPreferredSize, eventual getMinimumSize, getMaximumSize; • ad˘ugarea plan¸ei pe un container cu metoda add. a s • tratarea evenimentelor de tip FocusEvent, KeyEvent, MouseEvent, ComponentEvent, dac˘ este cazul. a Definirea generic˘ a unei plan¸e are urm˘torul format: a s a class Plansa extends Canvas implements ...Listener { //Eventual, unul sau mai multi constructori public Plansa() { ... } // Metode de desenare a componentei public void paint(Graphics g) { ... } // Metodele folosite de gestionarii de pozitionare public Dimension getPreferredSize() { // Dimensiunea implicita a plansei
  • 274.
    10.1. CONCEPTUL DEDESENARE 273 return ...; } public Dimension getMinimumSize() { return ... } public Dimension getMaximumSize() { return ... } // Implementarea metodelor interfetelor de tip Listener ... } S˘ definim o plan¸˘ pe care desen˘m un p˘trat ¸i cercul s˘u circumscris, a sa a a s a colorate diferite. La fiecare click de mouse, vom interschimba cele dou˘ culori a ˆ ıntre ele. Listing 10.2: Folosirea clasei Canvas import java . awt .*; import java . awt . event .*; class Plansa extends Canvas { Dimension dim = new Dimension (100 , 100) ; private Color color [] = { Color . red , Color . blue }; private int index = 0; public Plansa () { this . addMouseListener ( new MouseAdapter () { public void mouseClicked ( MouseEvent e ) { index = 1 - index ; repaint () ; } }) ; } public void paint ( Graphics g ) { g . setColor ( color [ index ]) ; g . drawRect (0 , 0 , dim . width , dim . height ) ; g . setColor ( color [1 - index ]) ; g . fillOval (0 , 0 , dim . width , dim . height ) ; } public Dimension getPreferredSize () {
  • 275.
    274 CAPITOLUL 10. DESENAREA return dim ; } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; setSize (200 , 200) ; add ( new Plansa () , BorderLayout . CENTER ) ; } } public class TestCanvas { public static void main ( String args []) { new Fereastra ( " Test Canvas " ) . show () ; } } 10.2 Contextul grafic de desenare Inainte ca utilizatorul s˘ poat˘ desena, el trebuie s˘ obtin˘ un context grafic a a a ¸ a de desenare pentru suprafata c˘reia ˆ apartine regiunea pe care se va desena. ¸ a ıi ¸ Un context grafic este, de fapt, un obiect prin intermediul c˘ruia putem a controla procesul de desenare a unui obiect. In general, desenarea se poate face: • pe o portiune de ecran, ¸ • la imprimant˘ sau a •ˆ ıntr-o zon˘ virtual˘ de memorie. a a Un context grafic este specificat prin intermediul unui obiect de tip Graph- ics primit ca parametru ˆ metodele paint ¸i update. In functie de dispozi- ın s ¸ tivul fizic pe care se face afi¸area (ecran, imprimant˘, plotter, etc) metodele s a de desenare au implement˘ri interne diferite, transparente utilizatorului. a Clasa Graphics pune la dispozitie metode pentru: ¸ • primitive grafice: desenarea de figuri geometrice, texte ¸i imagini s • stabilirea propriet˘¸ilor contextului grafic, adic˘ stabilirea: at a
  • 276.
    10.2. CONTEXTUL GRAFICDE DESENARE 275 – culorii ¸i fontului curente cu care se face desenarea, s – originii coordonatelor suprafetei de desenare, ¸ – suprafetei ˆ care sunt vizibile componentelor desenate, ¸ ın – modului de desenare. 10.2.1 Propriet˘¸ile contextului grafic at La orice tip de desenare parametrii legati de culoare, font, etc. vor fi specificati ¸ ¸ pentru contextul grafic ˆ care se face desenarea ¸i nu vor fi trimi¸i ca ar- ın s s gumente metodelor respective de desenare. In continuare, enumer˘m aceste a propriet˘¸i ¸i metodele asociate lor din clasa Graphics. at s Proprietate Metode Culoarea de desenare Color getColor() void setColor(Color c) Fontul de scriere a textelor Font getFont() void setFont(Font f) Originea coordonatelor translate(int x, int y) Zona de decupare Shape getClip() (zona ˆ care sunt vizibile desenele) void setClip(Shape s) ın Modul de desenare void setXorMode(Color c) void setPaintMode(Color c) 10.2.2 Primitive grafice Prin primitive grafice ne vom referi ˆ continuare la metodele clasei Graphics, ın care permit desenarea de figuri geometrice ¸i texte. s Desenarea textelor se face cu uzual cu metoda drawString care prime¸te s ca argumente un ¸ir ¸i coltul din stˆnga-jos al textului. Textul va fi desenat s s ¸ a cu fontul ¸i culoarea curente ale contextului grafic. s // Desenam la coordonatele x=10, y=20; drawString("Hello", 10, 20); Desenarea figurilor geometrice se realizeaz˘ cu urm˘toarele metode: a a
  • 277.
    276 CAPITOLUL 10. DESENAREA Figur˘ geometric˘ a a Metode Linie drawLine drawPolyline Dreptunghi simplu drawRect fillRect clearRect Dreptunghi cu chenar draw3DRect ”ridicat” sau ”adˆncit” fill3DRect a Dreptunghi cu colturi ¸ drawRoundRect retunjite fillRoundRect Poligon drawPolygon fillPolygon Oval (Elips˘a drawOval fillOval Arc circular sau drawArc eliptic fillArc Metodele care ˆ ıncep cu ”fill” vor desena figuri geometrice care au interi- orul colorat, adic˘ ”umplut” cu culoarea curent˘ a contextului de desenare, a a ˆ timp ce metodele care ˆ ın ıncep cu ”draw” vor desena doar conturul figurii respective. 10.3 Folosirea fonturilor Dup˘ cum vazut, pentru a scrie un text pe ecran avem dou˘ posibilit˘¸i. a a at Prima dintre acestea este s˘ folosim o component˘ orientat˘-text, cum ar a a a fi Label, iar a doua s˘ apel˘m la metodele clasei Graphics de desenare a a a textelor, cum ar fi drawString. Indiferent de modalitatea aleas˘, putem a specifica prin intermediul fonturilor cum s˘ arate textul respectiv, acest lucru a realizˆndu-se prin metoda setFont fie din clasa Component, fie din Graphics. a Cei mai importanti parametri ce caracterizeaz˘ un font sunt: ¸ a • Numele fontului: Helvetica Bold, Arial Bold Italic, etc. • Familia din care face parte fontul: Helvetica, Arial, etc. • Dimensiunea fontului: ˆ altimea sa; ın˘ ¸ • Stilul fontului: ˆ ıngro¸at (bold), ˆ s ınclinat (italic);
  • 278.
    10.3. FOLOSIREA FONTURILOR 277 • Metrica fontului. Clasele care ofer˘ suport pentru lucrul cu fonturi sunt Font ¸i FontMetrics, a s ˆ continuare fiind prezentate modalit˘¸ile de lucru cu acestea. ın at 10.3.1 Clasa Font Un obiect de tip Font ˆ ıncapsuleaz˘ informatii despre toti parametrii unui a ¸ ¸ font, mai putin despre metrica acestuia. Constructorul uzual al clasei este ¸ cel care prime¸te ca argument numele fontului, dimensiunea ¸i stilul acestuia: s s Font(String name, int style, int size) Stilul unui font este specificat prin intermediul constantelor: Font.PLAIN, Font.BOLD, Font.ITALIC iar dimensiunea printr-un ˆ ıntreg, ca ˆ exemplele ın de mai jos: new Font("Dialog", Font.PLAIN, 12); new Font("Arial", Font.ITALIC, 14); new Font("Courier", Font.BOLD, 10); Folosirea unui obiect de tip Font se realizeaz˘ uzual astfel: a // Pentru componente etichetate Label label = new Label("Un text"); label.setFont(new Font("Dialog", Font.PLAIN, 12)); // In metoda paint(Graphics g) g.setFont(new Font("Courier", Font.BOLD, 10)); g.drawString("Alt text", 10, 20); O platform˘ de lucru are instalate, la un moment dat, o serie ˆ a ıntreag˘ de a fonturi care sunt disponibile pentru scrierea textelor. Lista acestor fonturi se poate obtine astfel: ¸ Font[] fonturi = GraphicsEnvironment. getLocalGraphicsEnvironment().getAllFonts(); Exemplul urmator afi¸eaz˘ lista tuturor fonturilor disponibile pe plat- s a forma curent˘ de lucru. Textul fiec˘rui nume de font va fi scris cu fontul s˘u a a a corespunz˘tor. a
  • 279.
    278 CAPITOLUL 10. DESENAREA Listing 10.3: Lucrul cu fonturi import java . awt .*; class Fonturi extends Canvas { private Font [] fonturi ; Dimension canvasSize = new Dimension (400 , 400) ; public Fonturi () { fonturi = Graphi c sE n v ir o nm e n t . g e t L o c a l G r a p h i c s E n v i r o n m e n t () . getAllFonts () ; canvasSize . height = (1 + fonturi . length ) * 20; } public void paint ( Graphics g ) { String nume ; for ( int i =0; i < fonturi . length ; i ++) { nume = fonturi [ i ]. getFontName () ; g . setFont ( new Font ( nume , Font . PLAIN , 14) ) ; g . drawString ( i + " . " + nume , 20 , ( i + 1) * 20) ; } } public Dimension getPreferredSize () { return canvasSize ; } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; ScrollPane sp = new ScrollPane () ; sp . setSize (400 , 400) ; sp . add ( new Fonturi () ) ; add ( sp , BorderLayout . CENTER ) ; pack () ; } } public class TestAllFonts { public static void main ( String args []) { new Fereastra ( " All fonts " ) . show () ; } }
  • 280.
    10.3. FOLOSIREA FONTURILOR 279 10.3.2 Clasa FontMetrics La afi¸area unui ¸ir cu metoda drawString trebuie s˘ specific˘m pozitia la s s a a ¸ care s˘ apar˘ textul respectiv pe ecran. In momentul ˆ care avem de afi¸at a a ın s mai multe ¸iruri consecutiv, sau unele sub altele, trebuie s˘ calcul˘m pozitiile s a a ¸ lor de afi¸are ˆ functie de lungimea ¸i ˆ ¸imea ˆ pixeli a celorlalte texte. s ın ¸ s ınalt ın Pentru aceasta este folosit˘ clasa FontMetrics. Un obiect din aceast˘ clas˘ a a a se construie¸te pornind de la un obiect de tip Font ¸i pune la dispozitie s s ¸ informatii despre dimensiunile ˆ pixeli pe care le au caracterele fontului ¸ ın respectiv ˆ ıntr-un anumit context de desenare. A¸adar, un obiect de tip FontMetrics ˆ s ıncapsuleaz˘ informatii despre a ¸ metrica unui font, cu alte cuvinte despre dimensiunile ˆ pixeli ale carac- ın terelor sale. Utilitatea principal˘ a acestei clase const˘ ˆ faptul c˘ permite a a ın a pozitionarea precis˘ a textelor pe o suprafat˘ de desenare, indiferent de fontul ¸ a ¸a folosit de acestea. Metrica unui font const˘ ˆ urm˘toarele atribute pe care le au caracterele a ın a sale: • Linia de baz˘: este linia dup˘ care sunt aliniate caracterele unui font; a a • Linia de ascendent˘: linia superioara pe care nu o depaseste nici un ¸a caracter din font • Linia de descendent˘: linia inferioar˘ sub care nu coboar˘ nici un car- ¸a a a acter din font; • Ascendentul: distanta ˆ ¸ ıntre linia de baz˘ ¸i linia de ascendent˘; as ¸a • Descendentul: distanta ˆ ¸ ıntre linia de baz˘ ¸i linia de descendent˘; as ¸a • L˘¸imea: l˘¸imea unui anumit caracter din font; at at • Distanta ˆ ¸ ıntre linii (”leading”): distanta optim˘ ˆ ¸ a ıntre dou˘ linii de text a scrise cu acela¸i font. s • In˘ltimea: distanta dintre liniile de baz˘ (leading+ascent+descent); a¸ ¸ a Figura de mai jos prezint˘ o imagine reprezentativ˘ asupra metricii unui a a font:
  • 281.
    280 CAPITOLUL 10. DESENAREA Reamintim c˘ la metoda drawString(String s, int x, int y) argu- a mentele x ¸i y repreznit˘ coltul din stˆnga-jos al textului. Ca s˘ fim mai s a ¸ a a preci¸i, y reprezint˘ pozitia liniei de baz˘ a textului care va fi scris. s a ¸ a Un context grafic pune la dispozitie o metod˘ special˘ getFontMetrics ¸ a a de creare a unui obiect de tip FontMetrics, pornind de la fontul curent al contextului grafic: public void paint(Graphics g) { Font f = new Font("Arial", Font.BOLD, 11); FontMetrics fm = g.getFontMetrics(); } Cele mai folosite metode ale clasei FontMetrics sunt: • getHeight - determin˘ ˆ altimea unei linii pe care vor fi scrise caractere a ın˘ ¸ ale unui font; • stringWidth - determin˘ l˘¸imea total˘ ˆ pixeli a unui ¸ir de caractere a at a ın s specificat; • charWidth - determin˘ l˘¸imea unui anumit caracter din font. a at In exemplul urm˘tor sunt afi¸ate pe ecran zilele s˘pt˘mˆnii ¸i lunile an- a s a a a s ului:
  • 282.
    10.3. FOLOSIREA FONTURILOR 281 Listing 10.4: Folosirea clasei FontMetrics import java . awt .*; class Texte extends Canvas { Dimension canvasSize = new Dimension (800 , 100) ; private String [] zile = { " Luni " , " Marti " , " Miercuri " , " Joi " , " Vineri " , " Sambata " , " Duminica " }; private String [] luni = { " Ianuarie " , " Februarie " , " Martie " , " Aprilie " , " Mai " , " Iunie " , " Iulie " , " August " , " Septembrie " , " Octombrie " , " Noiembrie " , " Decembrie " }; public void paint ( Graphics g ) { FontMetrics fm ; int x , y ; String etZile = " Zilele saptamanii : " , etLuni = " Lunile anului : " , text ; // Alegem un font si aflam metrica sa g . setFont ( new Font ( " Arial " , Font . BOLD , 20) ) ; fm = g . getFontMetrics () ; x = 0; y = fm . getHeight () ; g . drawString ( etZile , x , y ) ; x += fm . stringWidth ( etZile ) ; for ( int i =0; i < zile . length ; i ++) { text = zile [ i ]; if ( i < zile . length - 1) text += " , " ; g . drawString ( text , x , y ) ; x += fm . stringWidth ( text ) ; } // Schimbam fontul g . setFont ( new Font ( " Dialog " , Font . PLAIN , 14) ) ; fm = g . getFontMetrics () ; x = 0; y += fm . getHeight () ; g . drawString ( etLuni , x , y ) ; x += fm . stringWidth ( etLuni ) ; for ( int i =0; i < luni . length ; i ++) { text = luni [ i ]; if ( i < luni . length - 1)
  • 283.
    282 CAPITOLUL 10. DESENAREA text += " , " ; g . drawString ( text , x , y ) ; x += fm . stringWidth ( text ) ; } } public Dimension getPreferredSize () { return canvasSize ; } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; add ( new Texte () , BorderLayout . CENTER ) ; pack () ; } } public class TestFontMetrics { public static void main ( String args []) { Fereastra f = new Fereastra ( " FontMetrics " ) ; f . show () ; } } 10.4 Folosirea culorilor Orice culoare este format˘ prin combinatia culorilor standard ro¸u (red), a ¸ s verde (green) ¸i albastru (blue), la care se adaug˘ un anumit grad de transparent˘ s a ¸a (alpha). Fiecare din ace¸ti patru parametri poate varia ˆ s ıntr-un interval cuprins fie ˆ ıntre 0 ¸i 255 (dac˘ dorim s˘ specific˘m valorile prin numere s a a a ˆ ıntregi), fie ˆıntre 0.0 ¸i 1.0 (dac˘ dorim s˘ specific˘m valorile prin numere s a a a reale). O culoare este reprezentat˘ printr-o instant˘ a clasei Color sau a sub- a ¸a clasei sale SystemColor. Pentru a crea o culoare avem dou˘ posibilit˘¸i: a at • S˘ folosim una din constantele definite ˆ cele dou˘ clase; a ın a • S˘ folosim unul din constructorii clasei Color. a S˘ vedem mai ˆ ai care sunt constantele definite ˆ aceste clase: a ıntˆ ın
  • 284.
    10.4. FOLOSIREA CULORILOR 283 Color SystemColor black activeCaption blue activeCaptionBorder cyan activeCaptionText darkGray control gray controlHighlight green controlShadow lightGray contolText magenta desktop orange menu pink text red textHighlight white window yellow ... Observati c˘ ˆ clasa Color sunt definite culori uzuale din paleta standard ¸ a ın de culori, ˆ timp ce ˆ clasa SystemColor sunt definite culorile componen- ın ın telor standard (ferestre, texte, meniuri, etc) ale platformei curente de lucru. Folosirea acestor constante se face ca ˆ exemplele de mai jos: ın Color rosu = Color.red; Color galben = Color.yellow; Color fundal = SystemColor.desktop; Dac˘ nici una din aceste culori predefinite nu corespunde preferintelor noas- a ¸ tre, atunci putem crea noi culori prin intermediul constructorilor clasei Color: Color(float red, flot green, float blue) Color(flot red, float green, float blue, float alpha) Color(int red, int green, int blue) Color(int red, int green, int blue, int alpha) Color(int rgb) unde red, green, blue, alpha sunt valorile pentru ro¸u, verde, albastru ¸i s s transparent˘ iar parametrul ”rgb” de la ultimul constructor reprezint˘ un ¸a a ˆ ıntreg format din: bitii 16-23 ro¸u, 8-15 verde, 0-7 albastru. ¸ s a ıntre 0 − 255 pentru tipul int, respectiv Valorile argumentelor variaz˘ ˆ 0.0 − 1.0 pentru tipul float. Valoarea 255 (sau 1.0) pentru transparent˘ ¸a specific˘ faptul c˘ respectiva culoare este complet opac˘, iar valoarea 0 (sau a a a 0.0) specific˘ transparent˘ total˘. Implicit, culorile sunt complet opace. a ¸a a
  • 285.
    284 CAPITOLUL 10. DESENAREA // Exemple de folosire a constructorilor: Color alb = new Color(255, 255, 255); Color negru = new Color(0, 0, 0); Color rosu = new Color(255, 0, 0); Color rosuTransparent = new Color(255, 0, 0, 128); Metodele cele mai folosite ale clasei Color sunt: brighter Creeaz˘ o noua versiune a culorii curente a darker mai deschis˘, respectiv mai ˆ a ınchis˘ a getRed getGreen Determin˘ parametrii din care a getBlue este alcatuit˘ culoarea a getAlpha getRGB Determin˘ valoarea ce reprezint˘ culoarea a a respectiv˘ (bitii 16-23 ro¸u, 8-15 verde, 0-7 albastru) a ¸ s S˘ consider˘m o aplicatie cu ajutorul c˘reia putem vizualiza dinamic cu- a a ¸ a lorile obtinute prin diferite combinatii ale parametrilor ce formeaz˘ o culoare. ¸ ¸ a Aplicatia va ar˘ta astfel: ¸ a Listing 10.5: Folosirea clasei Color import java . awt .*; import java . awt . event .*; class Culoare extends Canvas { public Color color = new Color (0 , 0 , 0 , 255) ; Dimension canvasSize = new Dimension (150 , 50) ; public void paint ( Graphics g ) { g . setColor ( Color . black ) ;
  • 286.
    10.4. FOLOSIREA CULORILOR 285 g . setFont ( new Font ( " Arial " , Font . BOLD , 12) ) ; String text = " " ; text += " R = " + color . getRed () ; text += " G = " + color . getGreen () ; text += " B = " + color . getBlue () ; text += " A = " + color . getAlpha () ; g . drawString ( text , 0 , 30) ; g . setColor ( color ) ; g . fillRect (0 , 0 , canvasSize . width , canvasSize . height ) ; } public Dimension getPreferredSize () { return canvasSize ; } } class Fereastra extends Frame implements Ad ju st me nt Lis te ne r { private Scrollbar rValue , gValue , bValue , aValue ; private Culoare culoare ; public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; Panel rgbValues = new Panel () ; rgbValues . setLayout ( new GridLayout (4 , 1) ) ; rValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ; rValue . setBackground ( Color . red ) ; gValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ; gValue . setBackground ( Color . green ) ; bValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ; bValue . setBackground ( Color . blue ) ; aValue = new Scrollbar ( Scrollbar . HORIZONTAL , 0 , 1 , 0 , 256) ;
  • 287.
    286 CAPITOLUL 10. DESENAREA aValue . setValue (255) ; aValue . setBackground ( Color . lightGray ) ; rgbValues . add ( rValue ) ; rgbValues . add ( gValue ) ; rgbValues . add ( bValue ) ; rgbValues . add ( aValue ) ; rgbValues . setSize (200 , 100) ; add ( rgbValues , BorderLayout . CENTER ) ; culoare = new Culoare () ; add ( culoare , BorderLayout . NORTH ) ; pack () ; rValue . add Adjus t m e n t L i s t e n e r ( this ) ; gValue . add Adjus t m e n t L i s t e n e r ( this ) ; bValue . add Adjus t m e n t L i s t e n e r ( this ) ; aValue . add Adjus t m e n t L i s t e n e r ( this ) ; } public void ad ju stm en tV al u e C h an ge d ( AdjustmentEvent e ) { int r = rValue . getValue () ; int g = gValue . getValue () ; int b = bValue . getValue () ; int a = aValue . getValue () ; Color c = new Color (r , g , b , a ) ; culoare . color = c ; culoare . repaint () ; } } public class TestColor { public static void main ( String args []) { Fereastra f = new Fereastra ( " Color " ) ; f . show () ; } } 10.5 Folosirea imaginilor Aceasta este o imagine:
  • 288.
    10.5. FOLOSIREA IMAGINILOR 287 In AWT este posibil˘ folosirea imaginilor create extern ˆ format gif sau a ın jpeg. Orice imagine va fi reprezentat˘ ca o instant˘ a clasei Image. Aceasta a ¸a nu este o clas˘ de componente (nu extinde Component) ci implementeaz˘ a a obiecte care pot fi desenate pe suprafata unor componente cu metode specifice ¸ unui context grafic pentru componenta respectiva (similar modului cum se deseneaz˘ o linie sau un cerc). a 10.5.1 Afi¸area imaginilor s Afi¸area unei imagini presupune realizarea urm˘toarilor doi pa¸i: s a s 1. Crearea unui obiect de tip Image; 2. Afi¸area propriu-zis˘ ˆ s a ıntr-un context grafic; Crearea unui obiect de tip Image se face folosind o imagine dintr-un fi¸ier s fie aflat pe ma¸ina pe care se lucreaz˘, fie aflat la o anumit˘ adres˘ Web s a a a (URL). Metodele pentru ˆ arcarea unei imagini dintr-un fi¸ier se g˘sesc ınc˘ s a ˆ clasele Applet ¸i Toolkit, avˆnd ˆ a aceea¸i denumire getImage ¸i ın s a ıns˘ s s urm˘toarele formate: a Applet Toolkit getImage(URL url) getImage(URL url) getImage(URL url, String fisier) getImage(String fisier) Pentru a obtine un obiect de tip Toolkit se va folosi metoda getDefaultToolkit, ¸ ca ˆ exemplul de mai jos: ın Toolkit toolkit = Toolkit.getDefaultToolkit(); Image image1 = toolkit.getImage("poza.gif"); Image image2 = toolkit.getImage( new URL("http://www.infoiasi.ro/~acf/poza.gif"));
  • 289.
    288 CAPITOLUL 10. DESENAREA Metoda getImage nu verific˘ dac˘ fi¸ierul sau adresa specificata reprezint˘ a a s a o imagine valid˘ ¸i nici nu ˆ as ıncarc˘ efectiv imaginea ˆ memorie, aceste operatiuni a ın ¸ fiind f˘cute abia ˆ momentul ˆ care se va realiza afi¸area imaginii pentru a ın ın s prima dat˘. Metoda nu face decˆt s˘ creeze un obiect de tip Image care face a a a referint˘ la o anumit˘ imagine extern˘. ¸a a a Afi¸area unei imagini ˆ s ıntr-un context grafic se realizeaz˘ prin intermediul a metodei drawImage din clasa Graphics ¸i, ˆ general, va fi facut˘ ˆ metoda s ın a ın paint a unei componente. Cele mai uzuale formate ale metodei sunt: boolean drawImage(Image img, int x, int y, ImageObserver observer) boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) unde: • img este obiectul ce reprezint˘ imaginea; a • x, y sunt coordonatele stˆnga-sus la care va fi afi¸at˘ imaginea, relative a s a la spatiul de coordonate al contextului grafic; ¸ • observer este un obiect care ”observ˘” ˆ arcarea imaginii ¸i va fi in- a ınc˘ s format pe m˘sura derul˘rii acesteia; a a • width, heigth reprezint˘ ˆ ¸imea ¸i l˘¸imea la care trebuie scalat˘ a ınalt s at a imaginea (dac˘ lipsesc, imaginea va fi afi¸at˘ la dimensiunile ei reale); a s a • bgColor reprezint˘ culoarea cu care vor fi colorati pixelii transparenti a ¸ ¸ ai imaginii (poate s˘ lipseasc˘). a a In exemplul urm˘tor afi¸am aceea¸i imagine de trei ori, folosind forme a s s diferite ale metodei drawImage: Image img = Toolkit.getDefaultToolkit().getImage("taz.gif"); g.drawImage(img, 0, 0, this); g.drawImage(img, 0, 200, 100, 100, this); g.drawImage(img, 200, 0, 200, 400, Color.yellow, this);
  • 290.
    10.5. FOLOSIREA IMAGINILOR 289 Metoda drawImage returneaz˘ true dac˘ imaginea a fost afi¸at˘ ˆ ˆ a a s a ın ıntregime ¸i false ˆ caz contrar, cu alte cuvinte metoda nu a¸tept˘ ca o imagine s˘ s ın s a a fie complet afi¸at˘ ci se termin˘ imediat ce procesul de afi¸are a ˆ s a a s ınceput. In sectiunea urm˘toare vom detalia acest aspect. ¸ a 10.5.2 Monitorizarea ˆ arc˘rii imaginilor ınc˘ a In cazul ˆ care se afi¸eaz˘ o imagine care se g˘se¸te pe Internet sau imaginea ın s a a s afi¸ata este de dimensiuni mari se va observa c˘ aceasta nu apare complet de s a la ˆ ınceput ci este desenat˘ treptat, f˘r˘ interventia programatorului. Acest a aa ¸ lucru se ˆ ampl˘ deoarece metoda drawImage nu face decˆt s˘ declan¸eze ıntˆ a a a s procesul de ˆ arcare ¸i desenare a imaginii, dup˘ care red˘ imediat con- ınc˘ s a a trolul apelantului, lucru deosebit de util ˆ ıntrucˆt procesul de ˆ arcare a a ınc˘ unei imagini poate dura mult ¸i nu este de dorit ca ˆ acest interval de timp s ın (pˆn˘ la ˆ arcarea complet˘ a imaginii) aplicatia s˘ fie blocat˘. Ca urmare, a a ınc˘ a ¸ a a la apelul metodei drawImage va fi desenat˘ numai portiunea de imagine care a ¸ este disponibil˘ la momentul initial ¸i care poate fi incomplet˘. De aceea a ¸ s a trebuie s˘ existe un mecanism prin care componenta s˘ fie redesenat˘ au- a a a tomat ˆ momentul ˆ care au mai sosit informatii legate de imagine, pˆn˘ la ın ın ¸ a a afi¸area sa complet˘. Acest mecanism este realizat prin intermediul interfetei s a ¸ ImageObserver, implementat˘ de clasa Component ¸i deci de toate compo- a s nentele. Aceast˘ interfat˘ descrie obiecte care au ˆ a ¸a ınceput s˘ utilizeze o imag- a ine incomplet˘ ¸i care trebuie anuntate de noile date obtinute ˆ legatur˘ cu as ¸ ¸ ın a imaginea respectiv˘. a Interfata ImageObserver are o singur˘ metod˘ numit˘ imageUpdate, ¸ a a a ce va fi apelat˘ periodic de firul de executie (creat automat) care se ocup˘ a ¸ a cu ˆ arcarea imaginii. Formatul acestei metode este: ınc˘ boolean imageUpdate (Image img, int flags, int x, int y, int w, int h ) Implementarea implicit˘ const˘ dintr-un apel la metoda repaint pentru a a dreptunghiul specificat la apel ¸i care reprezint˘ zona din imagine pentru care s a se cunosc noi informatii. Intregul f lags furnizeaz˘ informatii despre starea ¸ a ¸ transferului. Aceste informatii pot fi aflate prin intermediul constantelor ¸ definite de interfat˘: ¸a
  • 291.
    290 CAPITOLUL 10. DESENAREA ABORT Inc˘rcarea imaginii a fost ˆ a ıntrerupt˘, a ˆ ınainte de completarea sa ALLBITS Imaginea a fost ˆıncarcat˘ complet a ERROR A ap˘rut o eroare ˆ timpul a ın ˆ arc˘rii imaginii ınc˘ a FRAMEBITS Toti bitii cadrului curent sunt disponibili ¸ ¸ HEIGHT In˘ltimea imaginii este disponibil˘ a¸ a PROPERTIES Propriet˘¸ile imaginii sunt disponibile at SOMEBITS Au fost receptionati noi pixeli ai imaginii ¸ ¸ WIDTH L˘¸imea imaginii este disponibil˘ at a Prezenta ˆ parametrul f lags a unui bit de valoare 1 pe pozitia reprezen- ¸ ın ¸ tata de o constant˘ ˆ a ınseamn˘ c˘ respectiva conditie este ˆ a a ¸ ındeplinit˘. a // Imaginea este completa (flags & ALLBITS) != 0 // Eroare sau transferul imaginii a fost intrerupt (flags & ERROR | ABORT ) != 0 Metoda imageUpdate poate fi redefint˘ de o component˘ pentru a per- a a sonaliza procesul de afi¸are al imaginii. Aceasta va fi apelat˘ apelat˘ asincron s a a de fiecare dat˘ cˆnd sunt disponibili noi pixeli. a a public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) { // Desenam imaginea numai daca toti bitii sunt disponibili if (( flags & ALLBITS) != 0) repaint(); // Daca sunt toti bitii nu mai sunt necesare noi update-uri return ( (flags & (ALLBITS | ABORT)) == 0); } De asemenea, se observ˘ c˘ metodele clasei Image pentru determinarea a a dimensiunilor unei imagini au ca argument un obiect de tip ImageObserver. int getHeight(ImageObserver observer) int getWidth(ImageObserver observer) Dac˘ desenarea se face folosind clasa Canvas, atunci argumentul observer a al metodelor referitoare la imagini va fi this.
  • 292.
    10.5. FOLOSIREA IMAGINILOR 291 10.5.3 Mecanismul de ”double-buffering” Tehnica de double-buffering implic˘ realizarea unui desen ˆ memorie ¸i apoi a ın s transferul s˘u pe ecran, pentru a elimina efectul nepl˘cut de ”clipire” (”flick- a a ering”) rezultat atunci cˆnd sunt efectuate redesen˘ri repetate la intervale a a mici de timp. O situatie frecvent˘ ˆ care se apeleaz˘ la double-buffering este ¸ a ın a crearea de animatii. ¸ Secventa general˘ de implementare a mecanismului de double-buffering ¸ a este urm˘toarea: a // Supradefinim update pentru a elimina stergerea desenului public void update(Graphics g) { paint(g); } public void paint(Graphics g) { // Desenam in memorie pe un obiect de tip Image // w si h sunt dimensiunile desenului Image img = createImage(w, h); Graphics gmem = img.getGraphics(); /* Realizam desenul folosind gmem gmem.setColor(...); gmem.fillOval(...); ... */ // Transferam desenul din memorie pe ecran // desenand de fapt imaginea creata g.drawImage(img, 0, 0, this); gmem.dispose(); } } 10.5.4 Salvarea desenelor ˆ format JPEG ın Pachetul com.sun.image.codec.jpeg din distributia standard Java ofer˘ ¸ a suport pentru salvarea unei imagini aflate ˆ memorie ˆ ın ıntr-un fi¸ier ˆ for- s ın
  • 293.
    292 CAPITOLUL 10. DESENAREA mat JPEG. O clas˘ responsabil˘ cu realizarea acestei operatiuni ar putea fi a a ¸ definit˘ astfel: a import com.sun.image.codec.jpeg.*; import java.awt.image.BufferedImage; import java.awt.*; import java.io.*; class JPEGWriter { static float quality = 0.9f; //intre 0 si 1 public static void write(BufferedImage img, String filename) { try { FileOutputStream out = new FileOutputStream(filename); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); JPEGEncodeParam jep = encoder.getDefaultJPEGEncodeParam(img); jep.setQuality(quality, false); // Folosim setarile de codare jpeg implicite encoder.setJPEGEncodeParam(jep); encoder.encode(img); out.close(); } catch( Exception e ) { e.printStackTrace(); } } } 10.5.5 Crearea imaginilor ˆ memorie ın In cazul ˆ care dorim s˘ folosim o anumit˘ imagine creat˘ direct din pro- ın a a a gram ¸i nu ˆ s ıncarcat˘ dintr-un fi¸ier vom folosi clasa MemoryImageSource, a s aflata ˆ pachetul java.awt.image. Pentru aceasta va trebui s˘ definim un ın a
  • 294.
    ˘ 10.6. TIPARIREA 293 vector de numere ˆ ıntregi ˆ care vom scrie valorile ˆ ın ıntregi (RGB) ale cu- lorilor pixelilor ce definesc imaginea noastr˘. Dimensiunea vectorului va a fi ˆ altimea ˆ ın˘ ¸ ınmultit˘ cu l˘¸imea ˆ pixeli a imaginii. Constructorul clasei ¸ a at ın MemoryImageSource este: MemoryImageSource(int w, int h, int[] pixeli, int off, int scan) unde: • w, h reprezint˘ dimensiunile imaginii (l˘¸imea ¸i ˆ altimea); a at s ın˘ ¸ • pixeli[] este vectorul cu culorile imaginii; • of f, scan reprezint˘ modalitatea de construire a matricii imaginii pornind a de la vectorul cu pixeli, normal aceste valori sunt off = 0, scan = w In exemplul urmator vom crea o imagine cu pixeli de culori aleatorii ¸i o s vom afi¸a pe ecran: s int w = 100; int h = 100; int[] pix = new int[w * h]; int index = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int red = (int) (Math.random() * 255); int green = (int) (Math.random() * 255); int blue = (int) (Math.random() * 255); pix[index++] = new Color(red, green, blue).getRGB(); } img = createImage(new MemoryImageSource(w, h, pix, 0, w)); g.drawImage(img, 0, 0, this); // g este un context grafic 10.6 Tip˘rirea a Tip˘rirea ˆ Java este tratat˘ ˆ aceea¸i manier˘ ca ¸i desenarea, singurul a ın a ın s a s lucru diferit fiind contextul grafic ˆ care se execut˘ operatiile. Pachetul ın a ¸ care ofer˘ suport pentru tip˘rire este java.awt.print, iar clasa principal˘ a a a care controleaz˘ procesul de tip˘rire este PrinterJob. O aplicatie va apela a a ¸ metode ale acestei clase pentru:
  • 295.
    294 CAPITOLUL 10. DESENAREA • Crearea unei sesiuni de tip˘rire (job); a • Invocarea dialogului cu utilizatorul pentru specificarea unor parametri legati de tip˘rire; ¸ a • Tip˘rirea efectiv˘. a a Orice component˘ care poate fi afi¸at˘ pe ecran poate fi ¸i tip˘rit˘. In gen- a s a s a a eral, orice informatii care trebuie atˆt afi¸ate cˆt ¸i tip˘rite, vor fi ˆ ¸ a s a s a ıncapsulate ˆ ıntr-un obiect grafic - component˘, care are o reprezentare vizual˘ descris˘ a a a de metoda paint ¸i care va specifica ¸i modalitatea de reprezentare a sa la s s imprimant˘. a Un obiect care va fi tip˘rit trebuie s˘ implementeze interfata Printable, a a ¸ care contine o singur˘ metod˘ print, responsabil˘ cu descrierea modalit˘¸ii ¸ a a a at de tip˘rire a obiectului. In cazul cˆnd imaginea de pe ecran coincide cu a a imaginea de la imprimant˘, metodele paint ¸i print pot specifica aceea¸i a s s secvent˘ de cod. In general, metoda print are urm˘torul format: ¸a a public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { // Descrirea imaginii obiectului ce va fi afisata la imprimanta // Poate fi un apel la metoda paint: paint(g) if (ceva nu este in regula}) { return Printable.NO_SUCH_PAGE; } return Printable.PAGE_EXISTS; } Pa¸ii care trebuie efectuati pentru tip˘rirea unui obiect sunt: s ¸ a 1. Crearea unei sesiuni de tip˘rire: PrinterJob.getPrinterJob a 2. Specificarea obiectului care va fi tip˘rit: setPrintable; acesta trebuie a s˘ implementeze interfata Printable; a ¸ 3. Optional, initierea unui dialog cu utilizatorul pentru precizarea unor ¸ ¸ parametri legati de tip˘rire: printDialog; ¸ a
  • 296.
    ˘ 10.6. TIPARIREA 295 4. Tip˘rirea efectiv˘: print. a a In exemplul urm˘tor vom defini un obiect care are aceea¸i reprezentare a s pe ecran cˆt ¸i la imprimant˘ (un cerc circumscris unui p˘trat, ˆ a s a a ınsotite de un text) ¸i vom tip˘ri obiectul respectiv. s a Listing 10.6: Tip˘rirea unei componente a import java . io .*; import java . awt .*; import java . awt . event .*; import java . awt . print .*; class Plansa extends Canvas implements Printable { Dimension d = new Dimension (400 , 400) ; public Dimension getPreferredSize () { return d ; } public void paint ( Graphics g ) { g . drawRect (200 , 200 , 100 , 100) ; g . drawOval (200 , 200 , 100 , 100) ; g . drawString ( " Hello " , 200 , 200) ; } public int print ( Graphics g , PageFormat pf , int pi ) throws PrinterException { if ( pi >= 1) return Printable . NO_SUCH_PAGE ; paint ( g ) ; g . drawString ( " Numai la imprimanta " , 200 , 300) ; return Printable . PAGE_EXISTS ; } } class Fereastra extends Frame implements ActionListener { private Plansa plansa = new Plansa () ; private Button print = new Button ( " Print " ) ; public Fereastra ( String titlu ) { super ( titlu ) ; addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) {
  • 297.
    296 CAPITOLUL 10. DESENAREA System . exit (0) ; } }) ; add ( plansa , BorderLayout . CENTER ) ; Panel south = new Panel () ; south . setLayout ( new FlowLayout ( FlowLayout . CENTER ) ) ; south . add ( print ) ; add ( south , BorderLayout . SOUTH ) ; print . addActionListe ner ( this ) ; pack () ; } public void actionPerformed ( ActionEvent e ) { // 1. Crearea unei sesiuni de tiparire PrinterJob printJob = PrinterJob . getPrinterJob () ; // 2. Stabilirea obiectului ce va fi tiparit printJob . setPrintable ( plansa ) ; // 3. Initierea dialogului cu utilizatorul if ( printJob . printDialog () ) { try { // 4. Tiparirea efectiva printJob . print () ; } catch ( PrinterException ex ) { System . out . println ( " Exceptie la tiparire ! " ) ; ex . printStackTrace () ; } } } } public class TestPrint { public static void main ( String args []) throws Exception { Fereastra f = new Fereastra ( " Test Print " ) ; f . show () ; } }
  • 298.
    ˘ 10.6. TIPARIREA 297 Tiparirea textelor O alt˘ variant˘ pentru tip˘rirea de texte este deschiderea unui flux c˘tre a a a a dispozitivul special reprezentat de imprimant˘ ¸i scrierea informatiilor, linie as ¸ cu linie, pe acest flux. In sistemul de operare Windows, imprimanta poate fi referit˘ prin ”lpt1”, iar ˆ Unix prin ”/dev/lp”. Observati c˘ aceast˘ a ın ¸ a a abordare nu este portabil˘, deoarece necesit˘ tratare special˘ ˆ functie de a a a ın ¸ sistemul de operare folosit. Listing 10.7: Tip˘rirea textelor a import java . io .*; import java . awt .*; class TestPrintText { public static void main ( String args []) throws Exception { // pentru Windows PrintWriter imp = new PrintWriter ( new FileWriter ( " lpt1 " ) ) ; // pentru UNIX // PrintWriter imp = new PrintWriter ( new FileWriter ("/ dev / lp ") ) ; imp . println ( " Test imprimanta " ) ; imp . println ( " ABCDE " ) ; imp . close () ; } }
  • 299.
    298 CAPITOLUL 10. DESENAREA
  • 300.
    Capitolul 11 Swing 11.1 Introducere 11.1.1 JFC Tehnologia Swing face parte dintr-un proiect mai amplu numit JFC (Java Foundation Classes) care pune la dispozitie o serie ˆ ¸ ıntreag˘ de facilit˘¸i pen- a at tru scrierea de aplicatii cu o interfat˘ grafic˘ mult ˆ ¸ ¸a a ımbog˘¸it˘ functional ¸i at a ¸ s estetic fat˘ de vechiul model AWT. In JFC sunt incluse urm˘toarele: ¸a a • Componente Swing Sunt componente ce ˆ ınlocuiesc ¸i ˆ acela¸i timp extind vechiul set oferit s ın s de modelul AWT. • Look-and-Feel Permite schimbarea ˆ a¸i¸˘rii ¸i a modului de interactiune cu aplicatia ınf˘t sa s ¸ ¸ ˆ functie de preferintele fiec˘ruia. Acela¸i program poate utiliza di- ın ¸ ¸ a s verse moduri Look-and-Feel, cum ar fi cele standard Windows, Mac, Java, Motif sau altele oferite de diver¸i dezvoltatori, acestea putˆnd fi s a interschimbate de c˘tre utilizator chiar la momentul executiei . a ¸ • Accessibility API Permite dezvoltarea de aplicatii care s˘ comunice cu dispozitive uti- ¸ a lizate de c˘tre persoane cu diverse tipuri de handicap, cum ar fi cititoare a de ecran, dispozitive de recunoa¸tere a vocii, ecrane Braille, etc. s • Java 2D API Folosind Java 2D pot fi create aplicatii care utilizeaz˘ grafic˘ la un ¸ a a 299
  • 301.
    300 CAPITOLUL 11. SWING nivel avansat. Clasele puse la dispozitie permit crearea de desene com- ¸ plexe, efectuarea de operatii geometrice (rotiri, scal˘ri, translatii, etc.), ¸ a ¸ prelucrarea de imagini, tip˘rire, etc. a • Drag-and-Drop Ofer˘ posibilitatea de a efectua operatii drag-and-drop ˆ a ¸ ıntre aplicatii ¸ Java ¸i aplicatii native. s ¸ • Internationalizare ¸ Internationalizarea ¸i localizarea aplicatiilor sunt dou˘ facilit˘¸i extrem ¸ s ¸ a at de importante care permit dezvoltarea de aplicatii care s˘ poat˘ fi con- ¸ a a figurate pentru exploatarea lor ˆ diverse zone ale globului, utilizˆnd ın a limba ¸i particularit˘¸ile legate de formatarea datei, numerelor sau a s at monedei din zona respectiv˘. a In aceste capitol vom face o prezentare scurt˘ a componentelor Swing, a deoarece prezentarea detaliata a tuturor facilit˘¸ilor oferite de JFC ar oferi at suficient material pentru un volum de sine st˘t˘tor. aa 11.1.2 Swing API Unul din principalele deziderate ale tehnologiei Swing a fost s˘ pun˘ la a a dispozitie un set de componente GUI extensibile care s˘ permit˘ dezvoltarea ¸ a a rapid˘ de aplicatii Java cu interfat˘ grafic˘ competitiv˘ din punct de vedere a ¸ ¸a a a comercial. Pentru a realiza acest lucru, API-ul oferit de Swing este deosebit de complex avˆnd 17 pachete ˆ care se g˘sesc sute de clase ¸i interfete. Lista a ın a s ¸ complet˘ a pacehetelor din distributia standard 1.4 este dat˘ ˆ tabelul de a ¸ a ın mai jos: javax.accessibility javax.swing.plaf javax.swing.text.html javax.swing javax.swing.plaf.basic javax.swing.text.parser javax.swing.border javax.swing.plaf.metal javax.swing.text.rtf javax.swing.colorchooser javax.swing.plaf.multi javax.swing.tree javax.swing.event javax.swing.table javax.swing.undo javax.swing.filechooser javax.swing.text
  • 302.
    11.1. INTRODUCERE 301 Evident, nu toate aceste pachete sunt necesare la dezvolatarea unei aplicatii, ¸ cel mai important ¸i care contine componentele de baz˘ fiind javax.swing. s ¸ a Componentele folosite pentru crearea interfetelor grafice Swing pot fi gru- ¸ pate astfel: • Componente atomice JLabel, JButton, JCheckBox, JRadioButton, JToggleButton, JScrollBar, JSlider, JProgressBar, JSeparator • Componente complexe JTable, JTree, JComboBox, JSpinner, JList, JFileChooser, JColorChooser, JOptionPane • Componente pentru editare de text JTextField, JFormattedTextField, JPasswordField, JTextArea, JEditorPane, JTextPane • Meniuri JMenuBar, JMenu, JPopupMenu, JMenuItem, JCheckboxMenuItem, JRadioButtonMenuItem • Containere intermediare JPanel, JScrollPane, JSplitPane, JTabbedPane, JDesktopPane, JToolBar • Containere de nivel ˆ ınalt JFrame, JDialog, JWindow, JInternalFrame, JApplet 11.1.3 Asem˘n˘ri ¸i deosebiri cu AWT a a s Nu se poate spune c˘ Swing ˆ a ınlocuie¸te modelul AWT ci ˆ extinde pe acesta s ıl din urm˘ ad˘ugˆndu-i noi componente care fie ˆ a a a ınlocuiesc unele vechi fie sunt cu totul noi. O conventie ˆ general respectat˘ este prefixarea numelui unei ¸ ın a clase AWT cu litera ”J” pentru a denumi clasa corespondent˘ din Swing. a Astfel, ˆ locul clasei java.awt.Button putem folosi javax.swing.JButton, ın ˆ loc de java.awt.Label putem folosi javax.swing.JLabel, etc. Este reco- ın mandat ca o aplicatie cu interfat˘ grafic˘ s˘ foloseasc˘ fie componente AWT, ¸ ¸a a a a fie Swing, amestecarea lor fiind mai putin uzual˘. ¸ a
  • 303.
    302 CAPITOLUL 11. SWING Aplicatiile GUI vor avea ˆ continuare nevoie de pachetul java.awt deoarece ¸ ın aici sunt definite unele clase utilitare cum ar fi Color, Font, Dimension, etc. care nu au fost rescrise ˆ Swing. ın De asemenea, pachetul java.awt.event r˘mˆne ˆ continuare esential a a ın ¸ pentru tratarea evenimentelor generate atˆt de componente AWT cˆt ¸i de a a s cele din Swing. Pe lˆng˘ acesta mai poate fi necesar ¸i javax.swing.event a a s care descrie tipuri de evenimente specifice unor componente Swing, mecan- ismul de tratare a lor fiind ˆ a acela¸i ca ˆ AWT. ıns˘ s ın Pozitionarea componentelor este preluat˘ din AWT, fiind ad˘ugate ˆ a ¸ a a ıns˘ noi clase care descriu gestionari de pozitionare ˆ completarea celor exis- ¸ ın tente, cum ar fi BoxLayout ¸i SpringLayout. Difer˘ ˆ a modul de lucru cu s a ıns˘ containere, dup˘ cum vom vedea ˆ sectiunea dedicat˘ acestora. a ın ¸ a Majoritatea componentelor Swing care permit afi¸area unui text ca parte s a reprezent˘rii lor GUI pot specifica acel text fie ˆ mod normal folosind un a ın anumit font ¸i o anumit˘ culoare ce pot fi setate cu metodele setFont ¸i s a s setColor, fie prin intermediul limbajului HTML. Folosirea HTML aduce o flexibilitatea deosebit˘ ˆ realizarea interfetei grafice, ˆ a ın ¸ ıntrucˆt putem aplica a format˘ri multiple unui text, descompunerea acestuia pe mai multe linii, etc., a singurul dezavantaj fiind ˆ ıncetinirea etapei de afi¸are a componentelor. s JButton simplu = new JButton("Text simplu"); JButton html = new JButton( "<html><u>Text</u> <i>formatat</i></html>"); S˘ descriem o aplictie simpl˘ folosind AWT ¸i apoi Swing, pentru a ne a ¸ a s crea o prim˘ impresie asupra diferentelor ¸i asem˘n˘rilor dintre cele dou˘ a ¸ s a a a modele. Listing 11.1: O aplicatie simpl˘ AWT ¸ a import java . awt .*; import java . awt . event .*; public class ExempluAWT extends Frame implements ActionListener { public ExempluAWT ( String titlu ) { super ( titlu ) ; setLayout ( new FlowLayout () ) ; add ( new Label ( " Hello AWT " ) ) ; Button b = new Button ( " Close " ) ; b . addActionListener ( this ) ;
  • 304.
    11.1. INTRODUCERE 303 add ( b ) ; pack () ; show () ; } public void actionPerformed ( ActionEvent e ) { System . exit (0) ; } public static void main ( String args []) { new ExempluAWT ( " Hello " ) ; } } Listing 11.2: Aplicatia rescris˘ folosind Swing ¸ a import javax . swing .*; import java . awt .*; import java . awt . event .*; public class ExempluSwing extends JFrame implements ActionListener { public ExempluSwing ( String titlu ) { super ( titlu ) ; // Metoda setLayout nu se aplica direct ferestrei getContentPane () . setLayout ( new FlowLayout () ) ; // Componentele au denumiri ce incep cu litera J // Textul poate fi si in format HTML getContentPane () . add ( new JLabel ( " < html > <u > Hello </ u > <i > Swing </ i > </ html > " ) ) ; JButton b = new JButton ( " Close " ) ; b . addActionListener ( this ) ; // Metoda add nu se aplica direct ferestrei getContentPane () . add ( b ) ; pack () ; show () ; } public void actionPerformed ( ActionEvent e ) { // Tratarea evenimentelor se face ca in AWT System . exit (0) ; } public static void main ( String args []) { new ExempluSwing ( " Hello " ) ; }
  • 305.
    304 CAPITOLUL 11. SWING } 11.2 Folosirea ferestrelor Pentru a fi afi¸ate pe ecran componentele grafice ale unei aplicatii trebuie s ¸ plasate pe o suprafat˘ de afi¸are (container). Fiecare component˘ poate fi ¸a s a continut˘ doar ˆ ¸ a ıntr-un singur container, ad˘ugarea ei pe o supraft˘ nou˘ a ¸a a de afi¸are determinˆnd eliminarea ei de pe vechiul container pe care fusese s a plasat˘. Intrucˆt containerele pot fi ˆ a a ıncapsulate ˆ alte containere, o com- ın ponent˘ va face parte la un moment dat dintr-o ierarhie. R˘d˘cina acestei a a a ierarhii trebuie s˘ fie un a¸a numit container de nivel ˆ a s ınalt, care este reprezen- tat de una din clasele JFrame, JDialog sau JApplet. Intrucˆt de appleturi a ne vom ocupa separat, vom analiza ˆ continuare primele dou˘ clase. ın a In general orice aplicatie Java independent˘ bazat˘ pe Swing contine ¸ a a ¸ cel putin un container de nivel ˆ ¸ ınalt reprezentat de fereastra principal˘ a a programului, instant˘ a clasei JFrame. ¸a Simplificat, un obiect care reprezint˘ o fereastr˘ Swing contine o zon˘ a a ¸ a care este rezervat˘ barei de meniuri ¸i care este situat˘ de obieci ˆ partea sa a s a ın superioar˘ ¸i corpul ferestrei pe care vor fi plasate componentele. Imaginea as de mai jos pune ˆ evident˘ aceast˘ separare, valabil˘ de altfel pentru orice ın ¸a a a container de nivel ˆ ınalt: Corpul ferestrei este o instant˘ a clasei Container ce poate fi obtinut˘ cu ¸a ¸ a metoda getContentPane. Plasarea ¸i aranjarea componentelor pe suprafata s ¸
  • 306.
    11.2. FOLOSIREA FERESTRELOR 305 ferestrei se va face deci folosind obiectul de tip Container ¸i nu direct fereas- s tra. A¸adar, de¸i este derivat˘ din Frame, clasa JFrame este folosit˘ ˆ s s a a ıntr-un mod diferit fat˘ de p˘rintele s˘u: ¸a a a Frame f = new Frame(); f.setLayout(new FlowLayout()); f.add(new Button("OK")); JFrame jf = new JFrame(); jf.getContentPane().setLayout(new FlowLayout()); jf.getContentPane().add(new JButton("OK")); Spre deosebire de Frame, un obiect JFrame are un comportament implicit la ˆ ınchiderea ferestrei care const˘ ˆ ascunderea ferestrei atunci cˆnd utiliza- a ın a torul apas˘ butonul de ˆ a ınchidere. Acest comportament poate fi modificat prin apelarea metodei setDefaultCloseOperation care prime¸te ca argu- s ment diverse constante ce se g˘sesc fie ˆ clasa WindowConstants, fie chiar a ın ˆ JFrame. ın jf.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Ad˘ugarea unei bare de meniuri se realizeaz˘ cu metoda setJMenuBar, a a care prime¸te o instant˘ de tip JMenuBar. Crearea meniurilor este similar˘ s ¸a a cu modelul AWT. 11.2.1 Ferestre interne Din punctul de vedere al folosirii ferestrelor, aplicatiile pot fi ˆ artite ˆ ¸ ımp˘ ¸ ın dou˘ categorii: a • SDI (Single Document Interface) • MDI (Multiple Document Interface) Programele din prima categorie gestioneaz˘ la un moment dat o singur˘ a a fereastr˘ ˆ care se g˘sesc componentele cu care interactioneaz˘ utilizatorul. a ın a ¸ a In a doua categorie, fereastra principal˘ a aplicatiei ˆ a ¸ ınglobeaz˘ la rˆndul ei a a alte ferestre, uzual cu functionalit˘¸i similare, ce permit lucrul concurent pe ¸ at mai multe planuri.
  • 307.
    306 CAPITOLUL 11. SWING In Swing, clasa JInternalFrame pune la dispozitie o modalitate de a ¸ crea ferestre ˆ cadrul altor ferestre. Ferestrele interne au aproximativ aceea¸i ın s ˆ a¸i¸are ¸i functionalitate cu ferestrele de tip JFrame, singura diferent˘ fiind ınf˘t s s ¸ ¸a modul de gestionare a acestora. Uzual, obiectele de tip JInternalFrame vor fi plasate pe un container de tip DesktopPane, care va fi apoi plasat pe o fereastr˘ de tip JFrame. a Folosirea clasei DesktopPane este necesar˘ deoarece aceasta ”¸tie” cum s˘ a s a gestioneze ferestrele interne, avˆnd ˆ vedere c˘ acestea se pot suprapune ¸i a ın a s la un moment dat doar una singur˘ este activ˘. a a Exemplul urm˘tor pune ˆ evident˘ modelul general de creare ¸i afi¸are a ın ¸a s s a ferestrelor interne: Listing 11.3: Folosirea ferestrelor interne import javax . swing .*; import java . awt .*; class FereastraPrinci pal a extends JFrame { public Fereastra Pr i nc i p al a ( String titlu ) { super ( titlu ) ; setSize (300 , 200) ; s e tD e f au l t C lo s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ; FereastraInterna fin1 = new FereastraInterna () ; fin1 . setVisible ( true ) ; FereastraInterna fin2 = new FereastraInterna () ; fin2 . setVisible ( true ) ; JDesktopPane desktop = new JDesktopPane () ; desktop . add ( fin1 ) ; desktop . add ( fin2 ) ; setContentPane ( desktop ) ; fin2 . moveToFront () ; } } class FereastraInterna extends JInternalFrame { static int n = 0; // nr . de ferestre interne static final int x = 30 , y = 30; public FereastraInterna () { super ( " Document # " + (++ n ) ,
  • 308.
    11.3. CLASA JCOMPONENT 307 true , // resizable true , // closable true , // maximizable true ) ; // iconifiable setLocation ( x *n , y * n ) ; setSize ( new Dimension (200 , 100) ) ; } } public class TestInternalFrame { public static void main ( String args []) { new FereastraPrincipala ( " Test ferestre interne " ) . show () ; } } Ferestrele create de acest program vor ar˘ta ca ˆ figura de mai jos: a ın 11.3 Clasa JComponent JComponent este superclasa tuturor componentelor Swing, mai putin a celor ¸ care descriu containere de nivel ˆınalt JFrame, JDialog, JApplet. Deoarece JComponent extinde clasa Container, deci ¸i Component, ea mo¸tene¸te functionalitatea s s s ¸ general˘ a containerelor ¸i componentelor AWT, furnizˆnd bineˆ ¸eles ¸i o se- a s a ınt s rie ˆ ıntreag˘ de noi facilit˘¸i. a at Dintre nout˘¸ile oferite de JComponent amintim: at • ToolTips Folosind metoda setToolTip poate fi ata¸at unei componente un text s cu explicatii legate de componenta respectiv˘. Cˆnd utilizatorul trece ¸ a a
  • 309.
    308 CAPITOLUL 11. SWING cu mouse-ul deasupra componentei va fi afi¸at, pentru o perioad˘ de s a timp, textul ajut˘tor specificat. a • Chenare Orice component˘ Swing poate avea unul sau mai multe chenare. Speci- a ficarea unui chenar se realizeaz˘ cu metoda setBorder. a • Suport pentru plasare ¸i dimensionare s Folosind metodele setPreferredSize, setMinimumSize, setMaximumSize, setAlignmentX, setAlignmentY pot fi controlati parametrii folositi ¸ ¸ de gestionarii de pozitionare pentru plasarea ¸i dimensionarea automat˘ ¸ s a a componentelor ˆ cadrul unui container. ın • Controlul opacit˘¸ii at Folosind metoda setOpaque vom specifica dac˘ o component˘ trebuie a a sau nu s˘ deseneze toti pixelii din interiorul s˘u. Implicit, valoarea a ¸ a propriet˘¸ii de opacitate este false, ceea ce ˆ at ınseamn˘ c˘ este posibil a a s˘ nu fie desenati unii sau chiar toti pixelii, permitˆnd pixelilor de sub a ¸ ¸ ¸a component˘ s˘ r˘mˆn˘ vizibili (componenta nu este opac˘). Valoarea a a a a a a propriet˘¸ii pentru clasele derivate din JComponent depinde ˆ general at ın de Look-and-Feel-ul folosit. • Asocierea de actiuni tastelor ¸ Pentru componentele Swing exist˘ posibilitatea de specifica anumite a actiuni care s˘ se execute atunci cˆnd utilizatorul apas˘ o anumit˘ ¸ a a a a combinatie de taste ¸i componenta respectiv˘ este activ˘ (are focus- ¸ s a a ul). Aceast˘ facilitate simplific˘ varianta initial˘ de lucru, ¸i anume a a ¸ a s tratarea evenimentelor de tip KeyEvent printr-un obiect KeyListener. • Double-Buffering Tehnica de double-buffering, care implic˘ desenarea componentei ˆ a ın memorie ¸i apoi transferul ˆ s ıntregului desen pe ecran, este implementat˘ a automat de componentele Swing, spre deosebire de cele AWT unde trebuia realizat˘ manual dac˘ era cazul. a a Exemplul urm˘tor ilustreaz˘ modul de folosire a cˆtorva dintre facilit˘¸ile a a a at amintite mai sus: Listing 11.4: Facilit˘¸i oferite de clasa JComponent at
  • 310.
    11.3. CLASA JCOMPONENT 309 import javax . swing .*; import javax . swing . border .*; import java . awt .*; import java . awt . event .*; class Fereastra extends JFrame { public Fereastra ( String titlu ) { super ( titlu ) ; getContentPane () . setLayout ( new FlowLayout () ) ; s e tD e f au l t C lo s e Op e r at i o n ( JFrame . EXIT_ON_CLOSE ) ; // Folosirea chenarelor Border lowered , raised ; TitledBorder title ; lowered = BorderFactory . c r e a t e L o w e r e d B e v e l B o r d e r () ; raised = BorderFactory . c r e a t e R a i s e d B e v e l B o r d e r () ; title = BorderFactory . c re at eT it led Bo rd er ( " Borders " ) ; final JPanel panel = new JPanel () ; panel . setPreferredSize ( new Dimension (400 ,200) ) ; panel . setBackground ( Color . blue ) ; panel . setBorder ( title ) ; getContentPane () . add ( panel ) ; JLabel label1 = new JLabel ( " Lowered " ) ; label1 . setBorder ( lowered ) ; panel . add ( label1 ) ; JLabel label2 = new JLabel ( " Raised " ) ; label2 . setBorder ( raised ) ; panel . add ( label2 ) ; // Controlul opacitatii JButton btn1 = new JButton ( " Opaque " ) ; btn1 . setOpaque ( true ) ; // implicit panel . add ( btn1 ) ; JButton btn2 = new JButton ( " Transparent " ) ; btn2 . setOpaque ( false ) ; panel . add ( btn2 ) ; // ToolTips label1 . setToolTipText ( " Eticheta coborata " ) ; label2 . setToolTipText ( " Eticheta ridicata " ) ;
  • 311.
    310 CAPITOLUL 11. SWING btn1 . setToolTipText ( " Buton opac " ) ; // Textul poate fi HTML btn2 . setToolTipText ( " < html > <b > Apasati < font color = red > F2 </ font > " + " cand butonul are <u > focusul </ u > " ) ; // Asocierea unor actiuni ( KeyBindings ) /* Apasarea tastei F2 cand focusul este pe butonul al doilea va determina schimbarea culorii panelului */ btn2 . getInputMap () . put ( KeyStroke . getKeyStroke ( " F2 " ) , " schimbaCuloare " ) ; btn2 . getActionMap () . put ( " schimbaCuloare " , new AbstractAction () { private Color color = Color . red ; public void actionPerformed ( ActionEvent e ) { panel . setBackground ( color ) ; color = ( color == Color . red ? Color . blue : Color . red ) ; } }) ; pack () ; } } public class TestJComponent { public static void main ( String args []) { new Fereastra ( " Facilitati JComponent " ) . show () ; } } 11.4 Arhitectura modelului Swing 11.5 Folosirea modelelor Modelul Swing este bazat pe o arhitectur˘ asem˘n˘toare cu MVC (model- a a a view-controller). Arhitectura MVC specific˘ descompunerea unei aplicatii a ¸ vizuale ˆ trei p˘rti separate: ın a¸ • Modelul - care va reprezenta datele aplicatiei. ¸
  • 312.
    11.5. FOLOSIREA MODELELOR 311 • Prezentarea - modul de reprezentare vizual˘ a datelor. a • Controlul - transformarea actiunilor utilizatorului asupra componen- ¸ telor vizuale ˆ evenimente care s˘ actualizeze automat modelul aces- ın a tora (datele). Din motive practice, ˆ Swing p˘rtile de prezentare ¸i control au fost cu- ın a¸ s plate deoarece exista o leg˘tur˘ prea strˆns˘ ˆ a a a a ıntre ele pentru a fi concepute ca entit˘¸i separate. A¸adar, arhitectura Swing este de fapt o arhitectur˘ at s a cu model separabil, ˆ care datele componentelor (modelul) sunt separate de ın reprezentarea lor vizual˘. Aceast˘ abordare este logic˘ ¸i din perspectiva a a a s faptului c˘, ˆ general, modul de concepere a unei aplicatii trebuie s˘ fie ori- a ın ¸ a entat asupra reprezent˘rii ¸i manipul˘rii informatiilor ¸i nu asupra interfetei a s a ¸ s ¸ grafice cu utilizatorul. Pentru a realiza separarea modelului de prezentare, fiec˘rui obiect core- a spunz˘tor unei clase ce descrie o component˘ Swing ˆ este asociat un obiect a a ıi care gestioneaz˘ datele sale ¸i care implementeaz˘ o interfat˘ care reprezint˘ a s a ¸a a modelul componentei respective. Dup˘ cum se observ˘ din tabelul de mai a a jos, componente cu reprezent˘ri diferite pot avea acela¸i tip de model, dar a s exist˘ ¸i componente care au asociate mai multe modele: as Model Component˘ a ButtonModel JButton, JToggleButton, JCheckBox, JRadioButton, JMenu, JMenuItem, JCheckBoxMenuItem, JRadioButtomMenuItem JComboBox ComboBoxModel BoundedRangeModel JProgressBar, JScrollBarm, JSlider JTabbedPane SingleSelectionModel ListModel JList ListSelectionModel JList JTable TableModel JTable TableColumnModel JTree TreeModel JTree TreeSelectionModel Document JEditorPane, JTextPane, JTextArea, JTextField, JPasswordField Fiecare component˘ are un model initial implicit, ˆ a are posibilitatea a ¸ ıns˘ de a-l ˆ ınlocui cu unul nou atunci cˆnd este cazul. Metodele care acceseaz˘ a a
  • 313.
    312 CAPITOLUL 11. SWING modelul unui obiect sunt: setModel, respectiv getModel, cu argumente specifice fiec˘rei componente ˆ parte. Crearea unei clase care s˘ reprezinte a ın a un model se va face extinzˆnd interfata corespunz˘toare ¸i implementˆnd a ¸ a s a metodele definite de aceasta sau extinzˆnd clasa implicit˘ oferit˘ de API-ul a a a Swing ¸i supradefinind metodele care ne intereseaz˘. Pentru modelele mai s a complexe, cum ar fi cele asociate claselor JTable, JTree sau JList exist˘ a clase abstracte care implementeaz˘ interfata ce descrie modelul respectiv De a ¸ exemplu, interfata model a clasei JList este ListModel care este imple- ¸ mentat˘ de clasele DefaultListModel ¸i AbstractListModel. In functie a s ¸ de necesit˘¸i, oricare din aceste clase poate fi extins˘ pentru a crea un nou at a model. Listing 11.5: Folosirea mai multor modele pentru o componenta import javax . swing .*; import javax . swing . border .*; import java . awt .*; import java . awt . event .*; class Fereastra extends JFrame implements ActionListener { String data1 [] = { " rosu " , " galben " , " albastru " }; String data2 [] = { " red " , " yellow " , " blue " }; int tipModel = 1; JList lst ; ListModel model1 , model2 ; public Fereastra ( String titlu ) { super ( titlu ) ; s e tD e f au l t C lo s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ; // Lista initiala nu are nici un model lst = new JList () ; getContentPane () . add ( lst , BorderLayout . CENTER ) ; // La apasara butonului schimbam modelul JButton btn = new JButton ( " Schimba modelul " ) ; getContentPane () . add ( btn , BorderLayout . SOUTH ) ; btn . addActionListene r ( this ) ; // Cream obiectele corespunzatoare celor doua modele model1 = new Model1 () ; model2 = new Model2 () ; lst . setModel ( model1 ) ;
  • 314.
    11.5. FOLOSIREA MODELELOR 313 pack () ; } public void actionPerformed ( ActionEvent e ) { if ( tipModel == 1) { lst . setModel ( model2 ) ; tipModel = 2; } else { lst . setModel ( model1 ) ; tipModel = 1; } } // Clasele corespunzatoare celor doua modele class Model1 extends Abst ractLis tModel { public int getSize () { return data1 . length ; } public Object getElementAt ( int index ) { return data1 [ index ]; } } class Model2 extends Abst ractLis tModel { public int getSize () { return data2 . length ; } public Object getElementAt ( int index ) { return data2 [ index ]; } } } public class TestModel { public static void main ( String args []) { new Fereastra ( " Test Model " ) . show () ; } } Multe componente Swing furnizeaz˘ metode care s˘ obtin˘ starea obiec- a a ¸ a tului f˘r˘ a mai fi nevoie s˘ obtinem instanta modelului ¸i s˘ apel˘m metodele aa a ¸ ¸ s a a
  • 315.
    314 CAPITOLUL 11. SWING acesteia. Un exemplu este metoda getValue a clasei JSlider care este de fapt un apel de genul getModel().getValue(). In multe situatii ˆ a, mai ¸ ıns˘ ales pentru clase cum ar fi JTable sau JTree, folosirea modelelor aduce flex- ibilitate sporit˘ programului ¸i este recomandat˘ utilizarea lor. a s a 11.5.1 Tratarea evenimentelor Modelele componentelor trebuie s˘ notifice aparitia unor schimb˘ri ale datelor a ¸ a gestionate astfel ˆ at s˘ poat˘ fi reactualizat˘ prezentarea lor sau s˘ fie ex- ıncˆ a a a a ecutat un anumti cod ˆ cadrul unui obiect de tip listener. In Swing, aceast˘ ın a notificare este realizat˘ ˆ dou˘ moduri: a ın a 1. Informativ (lightweight) - Modelele trimit un eveniment prin care sunt informati ascult˘torii c˘ a survenit o anumit˘ schimbare a datelor, f˘r˘ ¸ a a a aa a include ˆ eveniment detalii legate de schimbarea survenit˘. Obiectele ın a de tip listener vor trebui s˘ apeleze metode specifice componentelor pen- a tru a afla ce anume s-a schimbat. Acest lucru se realizeaz˘ prin intefata a ¸ ChangeListener iar evenimentele sunt de tip ChangeEvent, modelele care suport˘ aceast˘ abordare fiind BoundedRangeModel, ButtonModel ¸i a a s SingleSelectionModel. Model Listener Tip Eveniment BoundedRangeModel ChangeListener ChangeEvent ButtonModel ChangeListener ChangeEvent SingleSelectionModelModel ChangeListener ChangeEvent Interfata ChangeListener are o singur˘ metod˘: ¸ a a public void stateChanged(ChangeEvent e), singura informatie continut˘ ˆ eveniment fiind componenta surs˘. ¸ ¸ a ın a Inregistrarea ¸i eliminarea obiectelor de tip listener se realizeaz˘ cu metodele s a addChangeListener, respectiv removeChangeListener. JSlider slider = new JSlider(); BoundedRangeModel model = slider.getModel(); model.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { // Sursa este de tip BoundedRangeModel BoundedRangeModel m = (BoundedRangeModel)e.getSource(); // Trebuie sa interogam sursa asupra schimbarii
  • 316.
    11.5. FOLOSIREA MODELELOR 315 System.out.println("Schimbare model: " + m.getValue()); } }); Pentru u¸urinta program˘rii, pentru a nu lucra direct cu instanta modelu- s ¸ a ¸ lui, unele clase permit ˆ ınregistrarea ascult˘torilor direct pentru componenta a ˆ sine, singura diferent˘ fat˘ de varianta anterioar˘ constˆnd ˆ faptul c˘ ın ¸a ¸a a a ın a sursa evenimentului este acum de tipul componentei ¸i nu de tipul modelului. s Secventa de cod de mai sus poate fi rescris˘ astfel: ¸ a JSlider slider = new JSlider(); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { // Sursa este de tip JSlider JSlider s = (JSlider)e.getSource(); System.out.println("Valoare noua: " + s.getValue()); } }); 2. Consistent(statefull) - Modele pun la dispozitie interfete special- ¸ ¸ izate ¸i tipuri de evenimente specifice ce includ toate informatiile legate de s ¸ schimbarea datelor. Model Listener Tip Eveniment ListModel ListDataListener ListDataEvent ListSelectionModel ListSelectionListener ListSelectionEvent ComboBoxModel ListDataListener ListDataEvent TreeModel TreeModelListener TreeModelEvent TreeSelectionModel TreeSelectionListener TreeSelectionEvent TableModel TableModelListener TableModelEvent TableColumnModel TableColumnModelListener TableColumnModelEvent Document DocumentListener DocumentEvent Document UndoableEditListener UndoableEditEvent Folosirea acestor interfete nu difer˘ cu nimic de cazul general: ¸ a String culori[] = {"rosu", "galben", "albastru"); JList list = new JList(culori); ListSelectionModel sModel = list.getSelectionModel(); sModel.addListSelectionListener(
  • 317.
    316 CAPITOLUL 11. SWING new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { // Schimbarea este continuta in eveniment if (!e.getValueIsAdjusting()) { System.out.println("Selectie curenta: " + e.getFirstIndex()); } } }); 11.6 Folosirea componentelor Datorit˘ complexit˘¸ii modelului Swing, ˆ aceast˘ secttiune nu vom ˆ a at ın a ¸ ıncerca o abordare exhaustiv˘ a modului de utilizare a tuturor componentelor, ci vom a pune ˆ evident˘ doar aspectele specifice acestui model, subliniind diferentele ın ¸a ¸ ¸i ˆ s ımbun˘t˘¸irile fat˘ AWT. a at ¸a 11.6.1 Componente atomice In categoria componentelor atomice includem componentele Swing cu functionalitate ¸ simpl˘, a c˘ror folosire este ˆ general asem˘n˘toare cu a echivalentelor din a a ın a a AWT. Aici includem: • Etichete: JLabel • Butoane simple sau cu dou˘ st˘ri: JButton, JCheckBox, JRadioButton, a a JToggleButton; mai multe butoane radio pot fi grupate folosind clasa ButtonGroup, pentru a permite selectarea doar a unuia dintre ele. • Componente pentru progres ¸i derulare: JSlider, JProgressBar, JScrollBar s • Separatori: JSeparator Deoarece utilizarea acestora este ˆ general facil˘, nu vom analiza ˆ parte ın a ın aceste componente. 11.6.2 Componente pentru editare de text Componentele Swing pentru afi¸area ¸i editarea textelor sunt grupate ˆ s s ıntr-o ierarhie ce are ca r˘d˘cin˘ clasa JTextComponent din pachetul javax.swing.text. a a a
  • 318.
    11.6. FOLOSIREA COMPONENTELOR 317 Dup˘ cum se observ˘ din imaginea de mai sus, clasele pot ˆ artite ˆ a a ımp˘ ¸ ın trei categorii, corespunz˘toare tipului textului editat: a • Text simplu pe o singur˘ linie a – JTextField - Permite editarea unui text simplu, pe o singur˘ a linie. – JPasswordField - Permite editarea de parole. Textul acestora va fi ascuns, ˆ locul caracterelor introduse fiind afi¸at un caracter ın s simbolic, cum ar fi ’*’. – JFormattedTextField - Permite introducerea unui text care s˘ a respecte un anumit format, fiind foarte util˘ pentru citirea de a numere, date calendaristice, etc. Este folosit˘ ˆ a ımpreun˘ cu clase a utilitare pentru formatarea textelor, cum ar fi NumberFormatter, DateFormatter, MaskFormatter, etc. Valoarea continut˘ de o ¸ a astfel de component˘ va fi obtinut˘/setat˘ cu metodele getValue, a ¸ a a respectiv setValue ¸i nu cu cele uzuale getText, setText. s • Text simplu pe mai multe linii – JTextArea - Permite editarea unui text simplu, pe mai multe linii. Orice atribut legat de stil, cum ar fi culoarea sau fontul, se aplic˘ ˆ a ıntregului text ¸i nu poate fi specificat doar unei anumite s portiuni. Uzual, o component˘ de acest tip va fi inclus˘ ˆ ¸ a a ıntr-un container JScrollPane, pentru a permite navigarea pe vertical˘ a
  • 319.
    318 CAPITOLUL 11. SWING ¸i orizontal˘ dac˘ textul introdus nu ˆ s a a ıncape ˆ suprafata alocat˘ ın ¸ a obiectului. Acest lucru este valabil pentru toate componentele Swing pentru care are sens notiunea de navigare pe orizontal˘ ¸ a sau vertical˘, nici una neoferind suport intrinsec pentru aceast˘ a a operatiune. ¸ • Text cu stil ˆ ımbog˘¸it pe mai multe linii at – JEditorPane - Permite afi¸area ¸i editarea de texte scrise cu stil- s s uri multiple ¸i care pot include imagini sau chiar diverse alet com- s ponente. Implicit, urm˘toarele tipuri de texte sunt recunoscute: a text/plain, text/html ¸i text/rtf. Una din utiliz˘rile cele mai s a simple ale acestei clase este setarea documentului ce va fi afi¸at s cu metoda setPage, ce prime¸te ca argument un URL care poate s referi un fi¸ier text, HTML sau RTF. s – JTextPane - Aceast˘ clas˘ extinde JEditorPane, oferind diverse a a facilit˘¸i suplimentare pentru lucrul cu stiluri ¸i paragrafe. at s Clasa JTextComponent ˆ ıncearc˘ s˘ p˘streze cˆt mai multe similitudini cu a a a a clasa TextComponent din AWT, ˆ a exist˘ diferente notabile ˆ ıns˘ a ¸ ıntre cele dou˘, a componenta Swing avˆnd caracteristici mult mai complexe cum ar fi suport a pentru operatii de undo ¸i redo, tratarea evenimentelor generate de cursor ¸ s (caret), etc. Orice obiect derivat din JTextComponent este format din: • Un model, referit sub denumirea de document, care gestioneaz˘ starea a componentei. O referint˘ la model poate fi obtinut˘ cu metoda getDocument, ¸a ¸ a ce returneaz˘ un obiect de tip Document. a • O reprezentare, care este responsabil˘ cu afi¸area textului. a s • Un ’controller’, cunoscut sub numele de editor kit care permite scrierea ¸i citirea textului ¸i care permite definirea de actiuni necesare edit˘rii. s s ¸ a Exist˘ diferent fat˘ de AWT ¸i la nivelul trat˘rii evenimentelor generate a ¸ ¸a s a de componentele pentru editarea de texte. Dintre evenimentele ce pot fi generate amintim: • ActionEvent - Componentele derivate din JTextField vor genera un eveniment de acest tip la ap˘sarea tastei Enter ˆ c˘suta de editare a a ın a ¸ textului. Interfata care trebuie implementat˘ este ActionListener. ¸ a
  • 320.
    11.6. FOLOSIREA COMPONENTELOR 319 • CaretEvent - Este evenimentul generat la deplasarea cursorului ce ges- tioneaz˘ pozitia curent˘ ˆ text. Interfata corespunz˘toare CaretLis- a ¸ a ın ¸ a tener contine o singur˘ metod˘: caretUpdate ce va fi apelat˘ ori de ¸ a a a cˆte ori apare o schimbare. a • DocumentEvent - Evenimentele de acest tip sunt generate la orice schimbare a textului, sursa lor fiind documentul (modelul) componen- tei ¸i nu componenta ˆ sine. Interfata corespunz˘toare este Docu- s ın ¸ a mentListener, ce contine metodele: ¸ – insertUpdate - apelat˘ la ad˘ugarea de noi caractere; a a – removeUpdate - apelat˘ dup˘ o operatiune de ¸tergere; a a ¸ s – changedUpdate - apelat˘ la schimbarea unor atribute legate de a stilul textului. • PropertyChangeEvent - Este un eveniment comun tuturor com- ponentelor de tip JavaBean, fiind generat la orice schimbare a unei propriet˘¸i a componentei. Interfata corespunz˘toare este Property- at ¸ a ChangeListener, ce contine metoda propertyChange. ¸ 11.6.3 Componente pentru selectarea unor elemente In aceast˘ categorie vom include clasele care permit selectarea unor valori a (elemente) dintr-o serie prestabilit˘. Acestea sunt: JList, JComboBox ¸i a s JSpinner. Clasa JList Clasa JList descrie o list˘ de elemente dispuse pe una sau mai multe coloane, a din care utilizatorul poate selecta unul sau mai multe. Uzual un obiect de acest tip va fi inclus ˆ ıntr-un container de tip JScrollPane.
  • 321.
    320 CAPITOLUL 11. SWING Initializarea unei liste se realizeaz˘ ˆ mai multe modalit˘¸i: ¸ a ın at • Folosind unul din constructorii care primesc ca argument un vector de elemente. Object elemente[] = {"Unu", "Doi", new Integer(3), new Double(4)}; JList lista = new JList(elemente); • Folosind constructorul f˘r˘ argumente ¸i ad˘ugˆnd apoi elemente mod- aa s a a elului implicit listei: DefaultListModel model = new DefaultListModel(); model.addElement("Unu"); model.addElement("Doi"); model.addElement(new Integer(3)); model.addElement(new Double(4)); JList lista = new JList(model); • Folosind un model propriu, responsabil cu furnizarea elementelor lis- tei. Acesta este un obiect dintr-o clas˘ ce trebuie s˘ implementeze a a interfata ListModel, uzual fiind folosit˘ extinderea clasei predefinite ¸ a AbstractListModel ¸i supradefinirea metodelor: getElementAt care s furnizeaz˘ elementul de pe o anumit˘ positie din list˘, respectiv getSize a a ¸ a care trebuie s˘ returneze num˘rul total de elemente din list˘. Evident, a a a aceast˘ variant˘ este mai complex˘, oferind flexibilitate sporit˘ ˆ lu- a a a a ın crul cu liste. ModelLista model = new ModelLista(); JList lista = new JList(model);
  • 322.
    11.6. FOLOSIREA COMPONENTELOR 321 ... class ModelLista extends AbstractListModel { Object elemente[] = {"Unu", "Doi", new Integer(3), new Double(4)}; public int getSize() { return elemente.length; } public Object getElementAt(int index) { return elemente[index]; } } Gestiunea articolelor selectate dintr-o list˘ se realizeaz˘ prin intermediul a a unui model, acesta fiind un obiect de tip ListSelectionModel. Obiectele de tip JList genereaz˘ evenimente de tip ListSelectionEvent, interfata core- a ¸ spunz˘toare fiind ListSelectionListener ce contine metoda valueChanged a ¸ apelat˘ ori de cˆte ori va fi schimbat˘ selectia elementelor din list˘. a a a ¸ a class Test implements ListSelectionListener { ... public Test() { ... // Stabilim modul de selectie list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); /* sau SINGLE_INTERVAL_SELECTION MULTIPLE_INTERVAL_SELECTION */ // Adaugam un ascultator ListSelectionModel model = list.getSelectionModel(); model.addListSelectionListener(this); ... } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; int index = list.getSelectedIndex();
  • 323.
    322 CAPITOLUL 11. SWING ... } } Evident, clasa ofer˘ metode pentru selectarea unor elemente din cadrul a programului setSelectedIndex, setSelectedIndices, etc. ¸i pentru obtinerea s ¸ celor selectate la un moment dat getSelectedIndex, getSelectedIndices, etc.. O facilitate extrem de important˘ pe care o au listele este posibilitatea de a a stabili un renderer pentru fiecare articol ˆ parte. Implicit toate elementele ın listei sunt afi¸ate ˆ acela¸i fel, ˆ a acest lucru poate fi schimbat prin crearea s ın s ıns˘ unei clase ce implementeaz˘ interfata ListCellRenderer ¸i personalizeaz˘ a ¸ s a reprezentarea elementelor listei ˆ functie de diver¸i parametri. Interfata ın ¸ s ¸ ListCellRenderer contine o singur˘ metod˘ getListCellRendererCom- ¸ a a ponent ce returneaz˘ un obiect de tip Component. Metoda va fi apelat˘ ˆ a a ın parte pentru reprezentarea fiec˘rui element al listei. a class MyCellRenderer extends JLabel implements ListCellRenderer { public MyCellRenderer() { setOpaque(true); } public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setText(value.toString()); setBackground(isSelected ? Color.red : Color.white); setForeground(isSelected ? Color.white : Color.black); return this; } } Setarea unui anumit renderer pentru o list˘ se realizeaz˘ cu metoda setCell- a a Renderer. Clasa JComboBox Clasa JComboBox este similar˘ cu JList, cu deosebirea c˘ permite doar se- a a lectarea unui singur articol, acesta fiind ¸i singurul permanent vizibil. Lista s
  • 324.
    11.6. FOLOSIREA COMPONENTELOR 323 celorlalte elemente este afi¸at˘ doar la ap˘sarea unui buton marcat cu o s a a s˘geat˘, ce face parte integrant˘ din component˘. a a a a JComboBox functioneaz˘ dup˘ acelea¸i principii ca ¸i clasa JList. ¸ a a s s Initializarea se face dintr-un vector sau folosind un model de tipul Com- ¸ boBoxModel, fiecare element putˆnd fi de asemenea reprezentat diferit prin a intermediul unui obiect ce implementeaz˘ aceea¸i intefat˘ ca ¸i ˆ cazul lis- a s ¸a s ın telor: ListCellRenderer. O diferent˘ notabil˘ const˘ ˆ modul de selectare a unui articol, deoarece ¸a a a ın JComboBox permite ¸i editarea explicit˘ a valorii elementului, acest lucru fiind s a controlat de metoda setEditable. Evenimentele generate de obiectele JComboBox sunt de tip ItemEvent generate la navigarea prin list˘, respectiv ActionEvent generate la selectarea a efectiv˘ a unui articol. a Clasa JSpinner Clasa JSpinner ofer˘ posibilitatea de a selecta o anumit˘ valoare (element) a a dintr-un domeniu prestabilit, lista elementelor nefiind ˆ a vizibil˘. Este ıns˘ a folosit atunci cˆnd domeniul din care poate fi f˘cut˘ selectia este foarte mare a a a ¸ sau chiar nem˘rginit; de exemplu: numere intregi intre 1950 si 2050. Com- a ponenta contine dou˘ butoane cu care poate fi selectat urm˘torul, respectiv ¸ a a predecesorul element din domeniu. JSpiner se bazeaz˘ exclusiv pe folosirea unui model. Acesta este un a obiect de tip SpinnerModel, existˆnd o serie de clase predefinite ce imple- a menteaz˘ aceast˘ interfat˘ cum ar fi SpinnerListModel, SpinnerNumberModel a a ¸a sau SpinnerDateModel ce pot fi utilizate.
  • 325.
    324 CAPITOLUL 11. SWING Componentele de acest tip permit ¸i specificarea unui anumit tip de s editor pentru valorile elementelor sale. Acesta este instalat automat pen- tru fiecare din modelele standard amintite mai sus, fiind reprezentat de una din clasele JSpinner.ListEditor, JSpinner.NumberEditor, respectiv JSpinner.DateEditor, toate derivate din JSpinner.DefaultEditor. Fiecare din editoarele amintite permite diverse format˘ri specifice. a Evenimentele generate de obiectele de tip JSpinner sunt de tip ChangeEvent, generate la schimbarea st˘rii componentei. a 11.6.4 Tabele Clasa JTable permite crearea de componente care s˘ afi¸eze o serie de ele- a s mente ˆ ıntr-un format tabelar, articolele fiind dispuse pe linii ¸i coloane. Un s tabel poate fi folosit doar pentru afi¸area formatat˘ a unor date, dar este s a posibil˘ ¸i editarea informatiei din celulele sale. De asemenea, liniile tabelu- as ¸ lui pot fi marcate ca selectate, tipul selectiei fiind simplu sau compus, tabelele ¸ extinzˆnd astfel functionalitatea listelor. a ¸ De¸i clasa JTable se g˘se¸te ˆ pachetul javax.swing, o serie de clase ¸i s a s ın s interfete necesare lucrului cu tabele se g˘sesc ˆ pachetul javax.swing.table, ¸ a ın acesta trebuind a¸adar importat. s Initializarea unui tabel poate fi f˘cut˘ ˆ mai multe moduri. ¸ a a ın Cea mai simpl˘ variant˘ este s˘ folosim unul din constructorii care primesc a a a ca argumente elementele tabelului sub forma unei matrici sau a unei colectii ¸ de tip Vector ¸i denumirile capurilor de coloan˘: s a String[] coloane = {"Nume", "Varsta", "Student"}; Object[][] elemente = { {"Ionescu", new Integer(20), Boolean.TRUE}, {"Popescu", new Integer(80), Boolean.FALSE}}; JTable tabel = new JTable(elemente, coloane); Dup˘ cum se observ˘, tipul de date al elementelor de pe o coloan˘ este a a a de tip referint˘ ¸i poate fi oricare. In cazul ˆ care celulele tabelului sunt ¸a s ın
  • 326.
    11.6. FOLOSIREA COMPONENTELOR 325 editabile trebuie s˘ existe un editor potrivit pentru tipul elementului din a celula respectiv˘. Din motive de eficient˘, implementarea acestei clase este a ¸a orientat˘ la nivel de coloan˘, ceea ce ˆ a a ınseamn˘ c˘ articole de pe o coloan˘ a a a vor fi reprezentate la fel ¸i vor avea acela¸i tip de editor. s s A doua variant˘ de creare a unui tabel este prin implementarea modelu- a lui acestuia ˆ ıntr-o clas˘ separat˘ ¸i folosirea constructorului corespunz˘tor. a as a Interfata care descrie modelul clasei JTable este TableModel ¸i contine ¸ s ¸ metodele care vor fi interogate pentru obtinerea informatiei din tabel. Uzual, ¸ ¸ crearea unui model se face prin extinderea clasei predefinite AbstractTable- Model, care implementeaz˘ deja TableModel. Tot ceea ce trebuie s˘ facem a a este s˘ supradefinim metodele care ne intereseaz˘, cele mai utilizate fiind a a (primele trei trebuie obligatoriu supradefinite, ele fiind declarate abstracte ˆ ın clasa de baz˘): a • getRowCount - returneaz˘ num˘rul de linii ale tabelului; a a • getColumnCount - returneaz˘ num˘rul de coloane ale tabelului; a a • getValueAt - returneaz˘ elementul de la o anumit˘ linie ¸i coloan˘; a a s a • getColumnName - returneaz˘ denumirea fiec˘rei coloane; a a • isCellEditable - specific˘ dac˘ o anumit˘ celul˘ este editabil˘. a a a a a Modelul mai contine ¸i metoda setValueAt care poate fi folosit˘ pentru ¸ s a setarea explicit˘ a valorii unei celule. a ModelTabel model = new ModelTabel(); JTable tabel = new JTable(model); ... class ModelTabel extends AbstractTableModel { String[] coloane = {"Nume", "Varsta", "Student"}; Object[][] elemente = { {"Ionescu", new Integer(20), Boolean.TRUE}, {"Popescu", new Integer(80), Boolean.FALSE}}; public int getColumnCount() { return coloane.length; } public int getRowCount() {
  • 327.
    326 CAPITOLUL 11. SWING return elemente.length; } public Object getValueAt(int row, int col) { return elemente[row][col]; } public String getColumnName(int col) { return coloane[col]; } public boolean isCellEditable(int row, int col) { // Doar numele este editabil return (col == 0); } } Orice schimbare a datelor tabelului va genera un eveniment de tip Table- ModelEvent. Pentru a trata aceste evenimente va trebui s˘ implement˘m a a interfata TableModelListener ce contine metoda tableChanged. Inreg- ¸ ¸ istrarea unui listener va fi f˘cut˘ pentru modelul tabelului: a a public class Test implements TableModelListener { ... public Test() { ... tabel.getModel().addTableModelListener(this); ... } public void tableChanged(TableModelEvent e) { // Aflam celula care a fost modificata int row = e.getFirstRow(); int col = e.getColumn(); TableModel model = (TableModel)e.getSource(); Object data = model.getValueAt(row, col); ... } }
  • 328.
    11.6. FOLOSIREA COMPONENTELOR 327 Tabele ofer˘ posibilitatea de a selecta una sau mai multe linii, nu neap˘rat a a consecutive, gestiunea liniilor selectate fiind realizat˘ prin intermediul unui a model. Acesta este o instant˘ ce implementeaz˘, ˆ ¸a a ıntocmai ca la liste, interfata ¸ ListSelectionModel. Tratarea evenimentelor generate de schimbarea selectiei ¸ ˆ tabel se realizeaz˘ prin ˆ ın a ınregistrarea unui ascult˘tor de tip ListSelection- a Listener: class Test implements ListSelectionListener { ... public Test() { ... // Stabilim modul de selectie tabel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // Adaugam un ascultator ListSelectionModel model = tabel.getSelectionModel(); model.addListSelectionListener(this); ... } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; ListSelectionModel model = (ListSelectionModel)e.getSource(); if (model.isSelectionEmpty()) { // Nu este nici o linie selectata ... } else { int index = model.getMinSelectionIndex(); // Linia cu numarul index este prima selectata ... } } } Dup˘ cum am spus, celule unei coloane vor fi reprezentare la fel, fiecare a coloan˘ avˆnd asociat un obiect renderer responsabil cu crearea componen- a a
  • 329.
    328 CAPITOLUL 11. SWING tei ce descrie celulele sale. Un astfel de obiect implementeaz˘ interfata a ¸ TableCellRenderer, care are o singur˘ metod˘ getTableCellRender- a a erComponent, aceasta fiind responsabil˘ cu crearea componentelor ce vor a fi afi¸ate ˆ celulele unei coloane. Implicit, exist˘ o serie de tipuri de date s ın a cu reprezent˘ri specifice, cum ar fi: Boolean, Number, Double, Float, a Date, ImageIcon, Icon, restul tipurilor avˆnd o reprezentare standard ce a const˘ ˆ a ıntr-o etichet˘ cu reprezentarea obiectului ca ¸ir de caractere. Specifi- a s carea unui renderer propriu se realizeaz˘ cu metoda setDefaultRenderer, a ce asociaz˘ un anumit tip de date cu un obiect de tip TableRenderer. a public class MyRenderer extends JLabel implements TableCellRenderer { public Component getTableCellRendererComponent(...) { ... return this; } } O situatie similar˘ o reg˘sim la nivelul editorului asociat celulelor dintr-o ¸ a a anumit˘ coloan˘. Acesta este un obiect ce implementeaz˘ interfata Tree- a a a ¸ CellEditor, ce extinde interfata CellEditor care generalizeaz˘ conceptul ¸ a de celul˘ editabil˘ pe care ˆ vom mai reg˘si la arbori. Implicit, exist˘ o a a ıl a a serie de editoare standard pentru tipurile de date mentionate anterior, dar ¸ este posibil˘ specificarea unui editor propriu cu metoda setDefaultEditor. a Crearea unui editor propriu se realizeaz˘ cel mai simplu prin extinderea clasei a utilitare AbstractCellEditor, care implementeaz˘ CellEditor, plus imple- a mentarea metodei specifice din TreeCellEditor. public class MyEditor extends AbstractCellEditor implements TableCellEditor { // Singura metoda abstracta a parintelui public Object getCellEditorValue() { // Returneaza valoarea editata ... }
  • 330.
    11.6. FOLOSIREA COMPONENTELOR 329 // Metoda definita de TableCellEditor public Component getTableCellEditorComponent(...) { // Returneaza componenta de tip editor ... } } 11.6.5 Arbori Clasa JTree permite afi¸area unor elemente ˆ s ıntr-o manier˘ ierarhic˘. Ca a a orice component˘ Swing netrivial˘, un obiect JTree reprezint˘ doar o imagine a a a a datelor, informatia ˆ sine fiind manipulat˘ prin intermediul unui model. ¸ ın a La nivel structural, un arbore este format dintr-o r˘d˘cin˘, noduri interne - a a a care au cel putin un fiu ¸i noduri frunz˘ - care nu mai au nici un descendent. ¸ s a De¸i clasa JTree se g˘se¸te ˆ pachetul javax.swing, o serie de clase ¸i s a s ın s interfete necesare lucrului cu arbori se g˘sesc ˆ pachetul javax.swing.tree. ¸ a ın Clasa care modeleaz˘ notiunea de nod al arborelui este DefaultMutable- a ¸ TreeNode, aceasta fiind folosit˘ pentru toate tipurile de noduri. Crearea a unui arbore presupune a¸adar crearea unui nod (r˘d˘cina), instantierea unui s a a ¸ obiect de tip JTree cu r˘d˘cina creat˘ ¸i ad˘ugarea apoi de noduri frunz˘ ca a a as a a fii ai unor noduri existente. String text = "<html><b>Radacina</b></html>"; DefaultMutableTreeNode root = new DefaultMutableTreeNode(text); DefaultMutableTreeNode numere = new DefaultMutableTreeNode("Numere"); DefaultMutableTreeNode siruri =
  • 331.
    330 CAPITOLUL 11. SWING new DefaultMutableTreeNode("Siruri"); for(int i=0; i<3; i++) { numere.add(new DefaultMutableTreeNode(new Integer(i))); siruri.add(new DefaultMutableTreeNode("Sirul " + i)); } root.add(numere); root.add(siruri); JTree tree = new JTree(root); Dup˘ cum se observ˘, nodurile arborelui pot fi de tipuri diferite, reprezentarea a a lor implicit˘ fiind obtinut˘prin apelarea metodei toString pentru obiectului a ¸ a continut. De asemenea, este posibil˘ specificarea unui text ˆ format HTML ¸ a ın ca valoare a unui nod, acesta fiind reprezentat ca atare. Dac˘ varianta ad˘ug˘rii explicite a nodurilor nu este potrivit˘, se poate a a a a implementa o clas˘ care s˘ descrie modelul arborelui. Aceasta trebuie s˘ a a a implementeze intefata TreeModel. ¸ Scopul unei componente de tip arbore este ˆ general selectarea unui nod ın al ierarhiei. Ca ¸i ˆ cazul listelor sau a tabelelor, gestiunea elementelor s ın selectate se realizeaz˘ printr-un model, ˆ aceast˘ situatie interfata core- a ın a ¸ ¸ spunz˘toare fiind TreeSelectionModel. Arborii permit ˆ a ınregistrarea unor obiecte listener, de tip TreeSelectionListener, care s˘ trateze evenimentele a generate la schimbarea selectiei ˆ arbore. ¸ ın class Test implements TreeSelectionListener { ... public Test() { ... // Stabilim modul de selectie tree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION); // Adaugam un ascultator tree.addTreeSelectionListener(this); ... }
  • 332.
    11.6. FOLOSIREA COMPONENTELOR 331 public void valueChanged(TreeSelectionEvent e) { // Obtinem nodul selectat DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (node == null) return; // Obtinem informatia din nod Object nodeInfo = node.getUserObject(); ... } } Fiecare nod al arborelui este reprezentar prin intermediul unei clase ren- derer. Aceasta implementeaz˘ interfata TreeCellRenderer, cea folosit˘ a ¸ a implicit fiind DefaultTreeCellRenderer. Prin implementarea interfetei ¸ sau extinderea clasei implicite pot fi create modalit˘¸i de personalizare a at nodurilor arborelui ˆ functie de tipul sau valoarea acestora. ın ¸ Exist˘ ˆ a ¸i diverse metode de a schimba ˆ a¸i¸area unui arbore f˘r˘ s˘ a ıns˘ s ınf˘t s aa a cre˘m noi clase de tip TreeCellRenderer. Acestea sunt: a • setRootVisible - Specific˘ dac˘ r˘d˘cina e vizibil˘ sau nu; a a a a a • setShowsRootHandles - Specific˘ dac˘ nodurile de pe primul nivel au a a simboluri care s˘ permit˘ expandarea sau restrˆngerea lor. a a a • putClientProperty - Stabile¸te diverse propriet˘¸i, cum ar fi modul s at de reprezentare a relatiilor (liniilor) dintre nodurile p˘rinte ¸i fiu: ¸ a s tree.putClientProperty("JTree.lineStyle", "Angled"); // sau "Horizontal", "None" • Specificarea unei iconite pentru nodurile frunz˘ sau interne: ¸ a ImageIcon leaf = createImageIcon("img/leaf.gif"); ImageIcon open = createImageIcon("img/open.gif"); ImageIcon closed = createImageIcon("img/closed.gif");
  • 333.
    332 CAPITOLUL 11. SWING DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer(); renderer.setLeafIcon(leaf); renderer.setOpenIcon(open); renderer.setClosedIcon(closed); tree.setCellRenderer(renderer); 11.6.6 Containere Dup˘ cum ¸tim, containerele reprezint˘ suprafet de afi¸are pe care pot fi a s a ¸ s plasate ale componente, eventual chiar alte containere. Superclasa compo- nentelor de acest tip este Container, clas˘ despre care am mai discutat ˆ a ın capitolul dedicat modeluli AWT. Containerele pot fi ˆ artite ˆ dou˘ categorii: ımo˘ ¸ ın a 1. Containere de nivel ˆ ınalt - Acestea sunt JFrame, JDialog, JApplet ¸i reprezint˘ r˘d˘cinile ierarhiilor de componente ale unei aplicatii. s a a a ¸ 2. Containere intermediare - Reprezint˘ suprafete de afi¸are cu aju- a ¸ s torul c˘rora pot fi organizate mai eficient componentele aplicatiei, putˆnd a ¸ a fi imbricate. Cele mai importante clase care descriu astfel de containere sunt: – JPanel – JScrollPane – JTabbedPane – JSplitPane – JLayeredPane – JDesktopPane – JRootPane JPanel are aceea¸i functionalitate ca ¸i clasa Panel din AWT, fiind folosit s ¸ s pentru gruparea mai multor componente Swing ¸i plasarea lor ˆ s ımpreun˘ a pe o alt˘ suprafat˘ de afi¸are. Gestionarul de pozitionare implicit este a ¸a s ¸ FlowLayout, acesta putˆnd fi schimbat ˆ a chiar ˆ momentul construirii a ıns˘ ın
  • 334.
    11.6. FOLOSIREA COMPONENTELOR 333 obiectului JPanel sau ulterior cu metoda setLayout. Ad˘ugarea de compo- a nente se realizeaz˘ ca pentru orice container, folosind metoda add. a JPanel p = new JPanel(new BorderLayout()); /* Preferabil, deoarece nu mai este construit si un obiect de tip FlowLayout (implicit) */ p.add(new JLabel("Hello")); p.add(new JButton("OK")); ... JScrollPane este o clas˘ foarte important˘ ˆ arhitectura modelului a a ın Swing, deoarece ofer˘ suport pentru derularea pe orizontal˘ ¸i vertical˘ a a a s a componentelor a c˘ror reprezentare complet˘ nu ˆ a a ıncape ˆ suprafata aso- ın ¸ ciat˘, nici o component˘ Swing neoferind suport intrinsec pentru aceast˘ a a a operatie. ¸ String elemente[] = new String[100]; for(int i=0; i<100; i++) elemente[i] = "Elementul " + i; JList lista = new JList(elemente); JScrollPane sp = new JScrollPane(lista); frame.getContentPane().add(sp); JTabbedPane este util˘ pentru suprapunerea mai multor containere, a uzual panouri (obiecte de tip JPanel), pe acela¸i spatiu de afi¸are, selectarea s ¸ s
  • 335.
    334 CAPITOLUL 11. SWING unuia sau altui panou realizˆndu-se prin intermediul unor butoane dispuse a pe partea superioar˘ a componentei, fiecare panou avˆnd un astfel de bu- a a ton corespunz˘tor. Ca functionalitate, ofer˘ o implementare asem˘n˘toare a ¸ a a a gestionarului de pozitionare CardLayout. ¸ JTabbedPane tabbedPane = new JTabbedPane(); ImageIcon icon = new ImageIcon("smiley.gif"); JComponent panel1 = new JPanel(); panel1.setOpaque(true); panel1.add(new JLabel("Hello")); tabbedPane.addTab("Tab 1", icon, panel1, "Aici avem o eticheta"); tabbedPane.setMnemonicAt(0, KeyEvent.VK_1); JComponent panel2 = new JPanel(); panel2.setOpaque(true); panel2.add(new JButton("OK")); tabbedPane.addTab("Tab 2", icon, panel2, "Aici avem un buton"); tabbedPane.setMnemonicAt(1, KeyEvent.VK_2); JSplitPane permite crearea unui container care contine dou˘ compo- ¸ a nente dispuse fie una lˆng˘ cealalt˘, fie una sub alta ¸i separarea acestora a a a s prin intermediul unei bare care s˘ permit˘ configurarea suprafetei alocate a a ¸ fiec˘rei componente. a String elem[] = {"Unu", "Doi", "Trei" }; JList list = new JList(elem);
  • 336.
    11.6. FOLOSIREA COMPONENTELOR 335 JPanel panel = new JPanel(new GridLayout(3, 1)); panel.add(new JButton("Adauga")); panel.add(new JButton("Sterge")); panel.add(new JButton("Salveaza")); JTextArea text = new JTextArea( "Mai multe componente separate prinn" + "intermediul containerelor JSplitPane"); // Separam lista de grupul celor trei butoane JSplitPane sp1 = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, list, panel); // Separam containerul cu lista si butoanele // de componenta pentru editare de text JSplitPane sp2 = new JSplitPane( JSplitPane.VERTICAL_SPLIT, sp1, text); frame.getContentPane().add(sp2); 11.6.7 Dialoguri Clasa care descrie ferestre de dialog este JDialog, crearea unui dialog re- alizˆndu-se prin extinderea acesteia, ˆ a ıntocmai ca ˆ modelul AWT. In Swing ın exist˘ ˆ a o serie de clase predefinite ce descriu anumite tipuri de dialoguri, a ıns˘ extrem de utile ˆ majoritatea aplicatiilor. Acestea sunt: ın ¸ • JOptionPane - Permite crearea unor dialoguri simple, folosite pentru afi¸area unor mesaje, realizarea unor interog˘ri de confirmare/renuntare, s a ¸
  • 337.
    336 CAPITOLUL 11. SWING etc. sau chiar pentru introducerea unor valori, clasa fiind extrem de configurabil˘. Mai jos, sunt exemplificate dou˘ modalit˘¸i de utilizare a a at a clasei: JOptionPane.showMessageDialog(frame, "Eroare de sistem !", "Eroare", JOptionPane.ERROR_MESSAGE); JOptionPane.showConfirmDialog(frame, "Doriti inchiderea aplicatiei ? ", "Intrebare", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); • JFileChooser - Dialog standard care permite navigarea prin sistemul de fi¸iere ¸i selectarea unui anumit fi¸ier pentru operatii de deschidere, s s s ¸ respectiv salvare. • JColorChooser - Dialog standard pentru selectarea ˆ ıntr-o manier˘ a facil˘ a unei culori. a • ProgressMonitor - Clas˘ utilizat˘ pentru monitorizare˘ progresului a a a unei operatii consumatoare de timp. ¸ 11.7 Desenarea 11.7.1 Metode specifice Dup˘ cum ¸tim, desenarea unei componente este un proces care se executa a s automat ori de cˆte ori este necesar. Procesul ˆ sine este asem˘n˘tor celui a ın a a din modelul AWT, ˆ a exist˘ unele diferente care trebuie mentionate. ıns˘ a ¸ ¸ Orice component˘ se g˘se¸te ˆ a a s ıntr-o ierarhie format˘ de containere, r˘d˘cina a a a acestei fiind un container de nivel ˆ ınalt, cum ar fi o fereastr˘ sau suprafata a ¸ unui applet. Cu alte cuvinte, componenta este plasat˘ pe o suprafat˘ de a ¸a afi¸are, care la rˆndul ei poate fi plasat˘ pe alt˘ suprafat˘ ¸i a¸a mai departe. s a a a ¸a s s Cˆnd este necesar˘ desenarea componentei repsective, fie la prima sa afi¸are, a a s fie ca urmare a unor actiuni externe sau interne programului, operatia de de- ¸ ¸ senare va fi executat˘ pentru toate containerele, ˆ a ıncepˆnd cu cel de la nivelul a superior.
  • 338.
    11.7. DESENAREA 337 Desenarea se bazeaz˘ pe modelul AWT, metoda cea mai important˘ fiind a a paint, apelat˘ automat ori de cˆte ori este necesar. Pentru componentele a a Swing, aceast˘ metod˘ are ˆ a o implementare specific˘ ¸i nu trebuie a a ıns˘ a s supradefinit˘. Aceasta este responsabil˘ cu apelul metodelor Swing ce a a deseneaz˘ componenta ¸i anume: a s • paintComponent - Este principala metod˘ pentru desenare ce este a supradefinit˘ pentru fiecare component˘ Swing ˆ parte pentru a descrie a a ın reprezentarea sa grafic˘. Implicit, ˆ cazul ˆ care componenta este a ın ın opac˘ metoda deseneaz˘ suprafata sa cu culoarea de fundal, dup˘ care a a ¸ a va executa desenarea propriu-zis˘. a • paintBorder - Deseneaz˘ chenarele componentei (dac˘ exist˘). Nu a a a trebuie supradefinit˘. a • paintChildren - Solicit˘ desenarea componentelor continute de aceast˘ a ¸ a component˘ (dac˘ exist˘). Nu trebuie supradefinit˘. a a a a Metoda paint este responsabil˘ cu apelul metodelor amintite mai sus ¸i a s realizarea unor optimiz˘ri legate de procesul de desenare, cum ar fi imple- a mentarea mecanismului de double-buffering. De¸i este posibil˘ supradefinirea s a ei, acest lucru nu este recomandat, din motivele amintite mai sus. Ca ¸i ˆ AWT, dac˘ se dore¸te redesenarea explicit˘ a unei componente s ın a s a se va apela metoda repaint. In cazul ˆ care dimensiunea sau pozitia com- ın ¸ ponentei s-au schimbat, apelul metodei revalidate va precede apelul lui repaint. Atentie ¸ Intocmai ca ˆ AWT, desenarea este realizat˘ de firul de executie care se ın a ¸ ocup˘ cu transmiterea evenimentelor. Pe perioada ˆ care acesta este ocupat a ın cu transmiterea unui mesaj nu va fi f˘cut˘ nici o desenare. De asemenea, a a dac˘ acesta este blocat ˆ a ıntr-o operatiune de desenare ce consum˘ mult timp, ¸ a pe perioada respectiv˘ nu va fi transmis nici un mesaj. a
  • 339.
    338 CAPITOLUL 11. SWING 11.7.2 Consideratii generale ¸ In continuare vom prezenta cˆteva consideratii generale legate de diferite a ¸ aspecte ale desen˘rii ˆ cadrul modelului Swing. a ın Afi¸area imaginilor s In AWT afi¸area unei imagini era realizat˘ uzual prin supradefinirea clasei s a Canvas ¸i desenarea imaginii ˆ metoda paint a acesteia. In Swing, exist˘ s ın a cˆteva solutii mai simple pentru afi¸area unei imagini, cea mai utilizat˘ fiind a ¸ s a crearea unei etichete (JLabel) sau a unui buton (JButton) care s˘ aib˘ se- a a tat˘ o anumit˘ imagine pe suprafata sa. Imaginea respectiv˘ trebuie creat˘ a a ¸ a a folosind clasa ImageIcon. ImageIcon img = new ImageIcon("smiley.gif"); JLabel label = new JLabel(img); Transparenta ¸ Cu ajutorul metodei setOpaque poate fi controlat˘ opacitatea componentelor a Swing. Aceasta este o facilitate extrem de important˘ deoarece permite a crearea de componente care nu au form˘ rectangular˘. De exemplu, un a a buton circular va fi construit ca fiind transparent (setOpaque(false)) ¸i va s desena ˆ interiorul s˘u o elips˘ umplut˘ cu o anumit˘ culoare. Evident, este ın a a a a necesar˘ implementarea de cod specific pentru a trata ap˘sarea acestui tip a a de buton. Trabsparenta ˆ a vine cu un anumit pret, deoarece pentru componentele ¸ ıns˘ ¸ transparente vor trebui redesenate containerele pe care se g˘se¸te aceasta, a s ˆ ıncetinind astfel procesul de afi¸are. Din acest motiv, de fiecare dat˘ cˆnd s a a este cazul, se recomand˘ setarea componentelor ca fiind opace a (setOpaque(true)). Dimensiunile componentelor Dup˘ cum ¸tim, orice component˘ este definit˘ de o suprafat˘ rectangu- a s a a ¸a lar˘. Dimensiunile acestei pot fi obtinute cu metodele getSize, getWidth, a ¸ getHeight. Acestea includ ˆ a ¸i dimsniunile chenarelor, evident dac˘ aces- ıns˘ s a tea exist˘. Suprafata ocupat˘ de acestea poate fi aflat˘ cu metoda getInsets a ¸ a a
  • 340.
    11.7. DESENAREA 339 ce va returna un obiect de tip Insets ce specific˘ num˘rul de pixeli ocupati a a ¸ cu chenare ˆ jurul componentei. ın public void paintComponent(Graphics g) { ... Insets insets = getInsets(); int currentWidth = getWidth() - insets.left - insets.right; int currentHeight = getHeight() - insets.top - insets.bottom; ... } Contexte grafice Argumentul metodei paintComponent este de tip Graphics ce ofer˘ prim-a itivele standard de desenare. In majoritatea cazurilor ˆ a, argumentul este ıns˘ de fapt de tip Graphics2D, clas˘ ce extinde Graphics ¸i pune la dispozitie a s ¸ metode mai sofisitcate de desenare cunoscute sub numele de Java2D. Pentru a avea acces la API-ul Java2D, este suficient s˘ facem conversia argumentului a ce descrie contextul grafic: public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D)g; // Desenam apoi cu g2d ... } In Swing, pentru a eficientiza desenarea, obiectul de tip Graphics primit ca argument de metoda paintComponent este refolosit pentru desenarea com- ponentei, a chenarelor ¸i a fiilor s˘i. Din acest motiv este foarte important s a ca atunci cˆnd supradefinim metoda paintComponent s˘ ne asigur˘m c˘ la a a a a terminarea metodei starea obiectului Graphics este aceea¸i ca la ˆ s ınceput. Acest lucru poate fi realizat fie explicit, fie folosind o copie a contextului grafic primit ca argument: // 1.Explicit Graphics2D g2d = (Graphics2D)g; g2d.translate(x, y); // modificam contexul ... g2d.translate(-x, -y); // revenim la starea initiala
  • 341.
    340 CAPITOLUL 11. SWING // 2. Folosirea unei copii Graphics2D g2d = (Graphics2D)g.create(); g2d.translate(x, y); ... g2d.dispose(); 11.8 Look and Feel Prin sintagma ’Look and Feel’ (L&F) vom ˆ ¸elege modul ˆ care sunt de- ınt ın senate componentele Swing ¸i felul ˆ care acestea interactioneaz˘ cu uti- s ın ¸ a lizatorul. Posibilitatea de a alege ˆ ıntre diferite moduri L&F are avantajul de a oferi prezentarea unei aplicatii ˆ ¸ ıntr-o form˘ grafic˘ care s˘ corespund˘ a a a a preferintelor utilizatorilor. In principiu, variantele originale de L&F furnizate ¸ ˆ distributia standard ofereau modalitatea ca o interfat˘ Swing fie s˘ se ın ¸ ¸a a ˆ ıncadreze ˆ ansamblul grafic al sistemului de operare folosit, fie s˘ aib˘ un ın a a aspect specific Java. Orice L&F este descris de o clas˘ derivat˘ din LookAndFeel. Distributia a a ¸ standard Java include urm˘toarele clase ce pot fi utilizate pentru selectarea a unui L&F: • javax.swing.plaf.metal.MetalLookAndFeel Este varianta implicit˘ de L&F ¸i are un aspect specific Java. a s • com.sun.java.swing.plaf.windows.WindowsLookAndFeel Varianta specific˘ sistemelor de operare Windows. Incepˆnd cu versi- a a unea 1.4.2 exist˘ ¸i implementarea pentru Windows XP . as • com.sun.java.swing.plaf.mac.MacLookAndFeel Varianta specific˘ sistemelor de operare Mac. a • com.sun.java.swing.plaf.motif.MotifLookAndFeel Specific˘ interfata CDE/Motif. a ¸ • com.sun.java.swing.plaf.gtk.GTKLookAndFeel GTK+ reprezint˘ un standard de creare a interfetelor grafice dezvoltat a ¸ independent de limbajul Java. (GTK este acronimul de la GNU Image Manipulation Program Toolkit). Folosind acest L&F este posibil˘ ¸i a s
  • 342.
    11.8. LOOK ANDFEEL 341 specificarea unei anumite teme prin intermediul unui fi¸ier de resurse s sau folosind variabila swing.gtkthemefile, ca ˆ exemplul de mai jos: ın java -Dswing.gtkthemefile=temaSpecifica/gtkrc App Specificare unei anumite interfete L&F poate fi realizat˘ prin mai multe ¸ a modalit˘¸i. at Folosirea clasei UImanager Clasa UIManager pune la dispozitie o serie de metode statice pentru se- ¸ lectarea la momentul executiei a uni anumit L&F, precum ¸i pentru obtinerea ¸ s ¸ unor variante specifice: • getLookAndFeel - Obtine varianta curent˘, returnˆnd un obiect de tip ¸ a a LookAndFeel. • setLookAndFeel - Seteaz˘ modul curet L&F. Metoda prime¸te ca ar- a s gument un obiect dintr-o clas˘ derivat˘ din LookAndFeel, fie un ¸ir de a a s caractere cu numele complet al clasei L&F. • getSystemLookAndFeelClassName - Obtine variant˘ specific˘ sistemu- ¸ a a lui de operare folosit. In cazul ˆ care nu exist˘ nici o astfel de clas˘, ın a a returneaz˘ varianta standard. a • getCrossPlatformLookAndFeelClassName - Returneaz˘ interfata grafic˘ a ¸ a standard Java (JLF). // Exemple: UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); Setarea propriet˘¸ii swing.defaultlaf at Exist˘ posibilitatea de a specifica varianta de L&F a aplicatie direct de la a ¸ linia de comand˘ prin setarea proprietˆ¸ii swing.defaultlaf: a at
  • 343.
    342 CAPITOLUL 11. SWING java -Dswing.defaultlaf= com.sun.java.swing.plaf.gtk.GTKLookAndFeel App java -Dswing.defaultlaf= com.sun.java.swing.plaf.windows.WindowsLookAndFeel App O alt˘ variant˘ de a seta aceast˘ proprietate este schimbarea ei direct ˆ a a a ın fi¸ierul swing.properties situat ˆ subdirectorul lib al distributiei Java. s ın ¸ # Swing properties swing.defaultlaf= com.sun.java.swing.plaf.windows.WindowsLookAndFeel Ordinea ˆ care este aleas˘ clasa L&F este urm˘toarea: ın a a 1. Apelul explicit al metodei UIManager.setLookAndFeel ˆ ınaintea cre˘rii a unei componente Swing. 2. Proprietatea swing.defaultlaf specificat˘ de la linia de comand˘. a a 3. Proprietatea swing.defaultlaf specificat˘ ˆ fi¸ierul swing.properties. a ın s 4. Clasa standard Java (JLF). Exist˘ posibilitatea de a schimba varianta de L&F chiar ¸i dup˘ afi¸area a s a s componentelor. Acesta este un proces care trebuie s˘ actualizeze ierarhiile a de componente, ˆ ıncepˆnd cu containerele de nivel ˆ a ınalt ¸i va fi realizat prin s apelul metodei SwingUtilities.updateComponentTreeUI ce va primi ca argument r˘d˘cina unei ierarhii. Secventa care efectueaz˘ aceast˘ operatie a a ¸ a a ¸ pentru o fereastr˘ f este: a UIManager.setLookAndFeel(numeClasaLF); SwingUtilities.updateComponentTreeUI(f); f.pack();
  • 344.
    Capitolul 12 Fire deexecutie ¸ 12.1 Introducere Firele de executie fac trecerea de la programarea secvential˘ la programarea ¸ ¸ a concurent˘. Un program secvential reprezint˘ modelul clasic de program: are a ¸ a un ˆınceput, o secvent˘ de executie a instructiunilor sale ¸i un sfˆr¸it. Cu alte ¸a ¸ ¸ s as cuvinte, la un moment dat programul are un singur punct de executie. Un ¸ program aflat ˆ executie se nume¸te proces. Un sistem de operare monotask- ın ¸ s ing, cum ar fi fi MS-DOS, nu este capabil s˘ execute decˆt un singur proces a a la un moment dat, ˆ timp ce un sistem de operare multitasking, cum ar fi ın UNIX sau Windows, poate rula oricˆte procese ˆ acela¸i timp (concurent), a ın s folosind diverse strategii de alocare a procesorului fiec˘ruia dintre acestea. a Am reamintit acest lucru deoarece notiunea de fir de executie nu are sens ¸ ¸ decˆt ˆ cadrul unui sistem de operare multitasking. a ın Un fir de executie este similar unui proces secvential, ˆ sensul c˘ are ¸ ¸ ın a un ˆ ınceput, o secvent˘ de executie ¸i un sfˆr¸it. Diferenta dintre un fir de ¸a ¸ s as executie ¸i un proces const˘ ˆ faptul c˘ un fir de executie nu poate rula ¸ s a ın a ¸ independent ci trebuie s˘ ruleze ˆ cadrul unui proces. a ın Definitie ¸ Un fir de executie este o succesiune scevential˘ de instructiuni care se ¸ ¸ a ¸ execut˘ ˆ cadrul unui proces. a ın 343
  • 345.
    344 CAPITOLUL 12. FIRE DE EXECUTIE ¸ Program (proces) Program (proces) Un program ˆsi poate defini ˆ a nu doar un fir de executie ci oricˆte, ceea ı¸ ıns˘ ¸ a ce ˆınseamn˘ c˘ ˆ cadrul unui proces se pot executa simultan mai multe fire a a ın de executie, permitˆnd efectuarea concurent˘ a sarcinilor independente ale ¸ ¸a a acelui program. Un fir de executie poate fi asem˘nat cu o versiune redus˘ a unui proces, ¸ a a ambele rulˆnd simultan ¸i independent pe o structur˘ secvential˘ format˘ de a s a ¸ a a instructiunile lor. De asemenea, executia simultan˘ a firelor ˆ cadrul unui ¸ ¸ a ın proces este similar˘ cu executia concurent˘ a proceselor: sistemul de operare a ¸ a va aloca procesorul dup˘ o anumit˘ strategie fiec˘rui fir de executie pˆn˘ la a a a ¸ a a terminarea lor. Din acest motiv firele de executie mai sunt numite ¸i procese ¸ s usoare. Care ar fi ˆ a deosebirile ˆ ıns˘ ıntre un fir de executie ¸i un proces ? In primul, ¸ s rˆnd deosebirea major˘ const˘ ˆ faptul c˘ firele de executie nu pot rula decˆt a a a ın a ¸ a ˆ cadrul unui proces. O alt˘ deosebire rezult˘ din faptul c˘ fiecare proces ın a a a are propria sa memorie (propriul s˘u spatiu de adrese) iar la crearea unui nou a ¸ proces (fork) este realizat˘ o copie exact˘ a procesului p˘rinte: cod ¸i date, a a a s ˆ timp ce la crearea unui fir nu este copiat decˆt codul procesului p˘rinte, ın a a toate firele de executie avˆnd acces la acelea¸i date, datele procesului original. ¸ a s A¸adar, un fir mai poate fi privit ¸i ca un context de executie ˆ cadrul unui s s ¸ ın proces. Firele de executie sunt utile ˆ multe privinte, ˆ a uzual ele sunt folosite ¸ ın ¸ ıns˘ pentru executarea unor operatii consumatoare de timp f˘r˘ a bloca procesul ¸ aa principal: calcule matematice, a¸teptarea eliber˘rii unei resurse, desenarea s a componentelor unei aplicatii GUI, etc. De multe ori ori, firele ˆsi desf˘¸oar˘ ¸ ı¸ as a activitatea ˆ fundal ˆ a, evident, acest lucru nu este obligatoriu. ın ıns˘ 12.2 Crearea unui fir de executie ¸ Ca orice alt obiect Java, un fir de executie este o instant˘ a unei clase. Firele ¸ ¸a de executie definite de o clas˘ vor avea acela¸i cod ¸i, prin urmare, aceea¸i ¸ a s s s
  • 346.
    12.2. CREAREA UNUIFIR DE EXECUTIE ¸ 345 secventa de instructiuni. Crearea unei clase care s˘ defineasc˘ fire de executie ¸ ¸ a a ¸ poate fi facut˘ prin dou˘ modalit˘¸i: a a at • prin extinderea clasei Thread. • prin implementarea interfetei Runnable. ¸ Orice clas˘ ale c˘rei instante vor fi executate separat ˆ a a ¸ ıntr-un fir propriu trebuie declarat˘ ca fiind de tip Runnable. Aceasta este o interfat˘ care a ¸a contine o singur˘ metod˘ ¸i anume metoda run. A¸adar, orice clas˘ ce descrie ¸ a as s a fire de executie va contine metoda run ˆ care este implementat codul ce va fi ¸ ¸ ın rulat. Interfata Runnable este conceput˘ ca fiind un protocol comun pentru ¸ a obiectele care doresc s˘ execute un anumit cod pe durata existentei lor. a ¸ Cea mai important˘ clas˘ care implementeaz˘ interfata Runnable este a a a ¸ Thread. Aceasta implementeaz˘ un fir de executie generic care, implicit, nu a ¸ face nimic; cu alte cuvinte, metoda run nu contine nici un cod. Orice fir de ¸ executie este o instant˘ a clasei Thread sau a unei subclase a sa. ¸ ¸a 12.2.1 Extinderea clasei Thread Cea mai simpl˘ metod˘ de a crea un fir de executie care s˘ realizeze o anumit˘ a a ¸ a a actiune este prin extinderea clasei Thread ¸i supradefinirea metodei run a ¸ s acesteia. Formatul general al unei astfel de clase este: public class FirExcecutie extends Thread { public FirExcecutie(String nume) { // Apelam constructorul superclasei super(nume); } public void run() { // Codul firului de executie ... } } Prima metod˘ a clasei este constructorul, care prime¸te ca argument un a s ¸ir ce va reprezenta numele firului de executie. In cazul ˆ care nu vrem s˘ s ¸ ın a d˘m nume firelor pe care le cre˘m, atunci putem renunta la supradefinirea a a ¸
  • 347.
    346 CAPITOLUL 12. FIRE DE EXECUTIE ¸ acestui constructor ¸i s˘ folosim constructorul implicit, f˘r˘ argumente, care s a aa creeaz˘ un fir de executie f˘r˘ nici un nume. Ulterior, acesta poate primi un a ¸ aa nume cu metoda setName. Evident, se pot defini ¸i alti constructori, ace¸tia s ¸ s fiinde utili atunci cˆnd vrem s˘ trimitem diver¸i parametri de initializare a a s ¸ firului nostru. A dou˘ metod˘ este metoda run, ”inima” oric˘rui fir de a a a executie, ˆ care scriem efectiv codul care trebuie s˘ se execute. ¸ ın a Un fir de executie creat nu este automat pornit, lansarea s˘ fiind realizeaz˘ ¸ a a de metoda start, definit˘ ˆ clasa Thread. a ın // Cream firul de executie FirExecutie fir = new FirExecutie("simplu"); // Lansam in executie fir.start(); S˘ consider˘m ˆ continuare un exemplu ˆ care definim un fir de executie a a ın ın ¸ ce afi¸eaz˘ numerele ˆ s a ıntregi dintr-un interval, cu un anumit pas. Listing 12.1: Folosirea clasei Thread class AfisareNumere extends Thread { private int a , b , pas ; public AfisareNumere ( int a , int b , int pas ) { this . a = a ; this . b = b ; this . pas = pas ; } public void run () { for ( int i = a ; i <= b ; i += pas ) System . out . print ( i + " " ) ; } } public class TestThread { public static void main ( String args []) { AfisareNumere fir1 , fir2 ; fir1 = new AfisareNumere (0 , 100 , 5) ; // Numara de la 0 la 100 cu pasul 5 fir2 = new AfisareNumere (100 , 200 , 10) ;
  • 348.
    12.2. CREAREA UNUIFIR DE EXECUTIE ¸ 347 // Numara de la 100 la 200 cu pasul 10 fir1 . start () ; fir2 . start () ; // Pornim firele de executie // Ele vor fi distruse automat la terminarea lor } } Gˆndind secvential, s-ar crede c˘ acest program va afi¸a prima dat˘ nu- a ¸ a s a merele de la 0 la 100 cu pasul 5, apoi numerele de la 100 la 200 cu pasul 10, ˆ ıntrucˆt primul apel este c˘tre contorul fir1, deci rezultatul afi¸at pe ecran a a s ar trbui s˘ fie: a 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 100 110 120 130 140 150 160 170 180 190 200 In realitate ˆ a, rezultatul obtinut va fi o intercalare de valori produse de ıns˘ ¸ cele dou˘ fire ce ruleaz˘ simultan. La rul˘ri diferite se pot obtine rezultate a a a ¸ diferite deoarece timpul alocat fiec˘rui fir de executie poate s˘ nu fie acela¸i, a ¸ a s el fiind controlat de procesor ˆıntr-o manier˘ ”aparent” aleatoare. Un posibil a rezultat al programului de mai sus: 0 100 5 110 10 120 15 130 20 140 25 150 160 170 180 190 200 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 12.2.2 Implementarea interfetei Runnable ¸ Ce facem ˆ a cˆnd dorim s˘ cre˘m o clas˘ care instantiaz˘ fire de executie ıns˘ a a a a ¸ a ¸ dar aceasta are deja o superclas˘, ¸tiind c˘ ˆ Java nu este permis˘ mo¸tenirea a s a ın a s multipl˘ ? a class FirExecutie extends Parinte, Thread // incorect ! In acest caz, nu mai putem extinde clasa Thread ci trebuie s˘ implement˘m a a direct interfata Runnable. Clasa Thread implementeaz˘ ea ˆ a¸i interfata ¸ a ıns˘s ¸ Runnable ¸i, din acest motiv, la extinderea ei obtineam implementarea indi- s ¸ rect˘ a interfetei. A¸adar, interfat˘ Runnable permite unei clase s˘ fie activ˘, a ¸ s ¸a a a f˘r˘ a extinde clasa Thread. aa Interfata Runnable se g˘se¸te ˆ pachetul java.lang ¸i este definit˘ astfel: ¸ a s ın s a
  • 349.
    348 CAPITOLUL 12. FIRE DE EXECUTIE ¸ public interface Runnable { public abstract void run(); } Prin urmare, o clas˘ care instantiaz˘ fire de executie prin implementarea a ¸ a ¸ interfetei Runnable trebuie obligatoriu s˘ implementeze metoda run. O astfel ¸ a de clas˘ se mai nume¸te clas˘ activ˘ ¸i are urm˘toarea structur˘: a s a as a a public class ClasaActiva implements Runnable { public void run() { //Codul firului de executie ... } } Spre deosebire de modalitatea anterioar˘, se pierde ˆ a tot suportul oferit a ıns˘ de clasa Thread. Simpla instantiere a unei clase care implemeneaz˘ interfata ¸ a ¸ Runnable nu creeaz˘ nici un fir de executie, crearea acestora trebuind f˘cut˘ a ¸ a a explicit. Pentru a realiza acest lucru trebuie s˘ instantiem un obiect de tip a ¸ Thread ce va reprezenta firul de executie propriu zis al c˘rui cod se gase¸te ¸ a s ˆ clasa noastr˘. Acest lucru se realizeaz˘, ca pentru orice alt obiect, prin ın a a instructiunea new, urmat˘ de un apel la un constructor al clasei Thread, ˆ a ¸ a ıns˘ nu la oricare dintre ace¸tia. Trebuie apelat constructorul care s˘ primeasc˘ s a a drept argument o instant˘ a clasei noastre. Dup˘ creare, firul de executie ¸a a ¸ poate fi lansat printr-un apel al metodei start. ClasaActiva obiectActiv = new ClasaActiva(); Thread fir = new Thread(obiectActiv); fir.start(); Aceste operatiuni pot fi f˘cute chiar ˆ cadrul clasei noastre: ¸ a ın public class FirExecutie implements Runnable { private Thread fir = null; public FirExecutie() if (fir == null) { fir = new Thread(this);
  • 350.
    12.2. CREAREA UNUIFIR DE EXECUTIE ¸ 349 fir.start(); } } public void run() { //Codul firului de executie ... } } Specificarea argumentului this ˆ constructorul clasei Thread determin˘ ın a crearea unui fir de executie care, la lansarea sa, va apela metoda run din ¸ clasa curent˘. A¸adar, acest constructor accept˘ ca argument orice instant˘ a s a ¸a a unei clase ”Runnable”. Pentru clasa FirExecutie dat˘ mai sus, lansarea a firului va fi f˘cut˘ automat la instantierea unui obiect al clasei: a a ¸ FirExecutie fir = new FirExecutie(); Atentie ¸ Metoda run nu trebuie apelat˘ explicit, acest lucru realizˆndu-se automat a a la apelul metodei start. Apelul explicit al metodei run nu va furniza nici o eroare, ˆ a aceasta va fi executat˘ ca orice alt˘ metoda, ¸i nu separat ˆ ıns˘ a a s ıntr-un fir. S˘ consider˘m urm˘torul a a a exemplu ˆ care cre˘m dou˘ fire de executie ın a a ¸ folosind interfata Runnable. ¸ Fiecare fir va desena figuri geometrice de un anumit tip, pe o suprafat˘ de ¸a desenare de tip Canvas. Vom porni apoi dou˘ a fire de executie care vor rula ¸ concurent, desenˆnd figuri diferite, fiecare pe a suprafata sa. ¸ Listing 12.2: Folosirea interfetei Runnable ¸ import java . awt .*; import java . awt . event .*; class Plansa extends Canvas implements Runnable { // Deoarece Plansa extinde Canvas ,
  • 351.
    350 CAPITOLUL 12. FIRE DE EXECUTIE ¸ // nu mai putem extinde clasa Thread Dimension dim = new Dimension (300 , 300) ; Color culoare ; String figura ; int x =0 , y =0 , r =0; public Plansa ( String figura , Color culoare ) { this . figura = figura ; this . culoare = culoare ; } public Dimension getPreferredSize () { return dim ; } public void paint ( Graphics g ) { // Desenam un chenar g . setColor ( Color . black ) ; g . drawRect (0 , 0 , dim . width -1 , dim . height -1) ; // Desenam figura la coordonatele calculate // de firul de executie g . setColor ( culoare ) ; if ( figura . equals ( " patrat " ) ) g . drawRect (x , y , r , r ) ; else if ( figura . equals ( " cerc " ) ) g . drawOval (x , y , r , r ) ; } public void update ( Graphics g ) { paint ( g ) ; // Supradefinim update ca sa nu mai // fie stearsa suprafata de desenare } public void run () { /* Codul firului de executie : Afisarea a 100 de figuri geometrice la pozitii si dimensiuni calculate aleator . Intre doua afisari , facem o pauza de 50 ms */ for ( int i =0; i <100; i ++) {
  • 352.
    12.2. CREAREA UNUIFIR DE EXECUTIE ¸ 351 x = ( int ) ( Math . random () * dim . width ) ; y = ( int ) ( Math . random () * dim . height ) ; r = ( int ) ( Math . random () * 100) ; try { Thread . sleep (50) ; } catch ( InterruptedEx c e p t i o n e ) {} repaint () ; } } } class Fereastra extends Frame { public Fereastra ( String titlu ) { super ( titlu ) ; this . addWindowListener ( new WindowAdapter () { public void windowClosing ( WindowEvent e ) { System . exit (0) ; } }) ; // Cream doua obiecte active de tip Plansa Plansa p1 = new Plansa ( " patrat " , Color . blue ) ; Plansa p2 = new Plansa ( " cerc " , Color . red ) ; // Acestea extind Canvas , le plasam pe fereastra setLayout ( new GridLayout (1 , 2) ) ; add ( p1 ) ; add ( p2 ) ; pack () ; // Pornim doua fire de executie , care vor // actualiza desenul celor doua planse new Thread ( p1 ) . start () ; new Thread ( p2 ) . start () ; } } public class TestRunnable { public static void main ( String args []) { Fereastra f = new Fereastra ( " Test Runnable " ) ; f . show () ; } }
  • 353.
    352 CAPITOLUL 12. FIRE DE EXECUTIE ¸ 12.3 Ciclul de viat˘ al unui fir de executie ¸a ¸ Fiecare fir de executie are propriul s˘u ciclu de viat˘: este creat, devine activ ¸ a ¸a prin lansarea sa ¸i, la un moment dat, se termin˘. In continuare, vom analiza s a mai ˆındeaproape st˘rile ˆ care se poate g˘si un fir de executie. Diagrama a ın a ¸ de mai jos ilustreaz˘ generic aceste st˘ri precum ¸i metodele care provoaca a a s tranzitia dintr-o stare ˆ alta: ¸ ın A¸adar, un fir de executie se poate g˘si ˆ una din urm˘toarele patru s ¸ a ın a st˘ri: a • ”New Thread” • ”Runnable” • ”Not Runnable” • ”Dead” Starea ”New Thread” Un fir de executie se g˘se¸te ˆ aceast˘ stare imediat dup˘ crearea sa, cu alte ¸ a s ın a a cuvinte dup˘ instantierea unui obiect din clasa Thread sau dintr-o subclas˘ a ¸ a a sa. Thread fir = new Thread(obiectActiv); // fir se gaseste in starea "New Thread"
  • 354.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 353 In aceast˘ stare firul este ”vid”, el nu are alocate nici un fel de resurse sis- a tem ¸i singura operatiune pe care o putem executa asupra lui este lansarea s ¸ ˆ executie, prin metoda start. Apelul oric˘rei alte metode ˆ afar˘ de start ın ¸ a ın a nu are nici un sens ¸i va provoca o exceptie de tipul IllegalThreadStateException. s ¸ Starea ”Runnable” Dup˘ apelul metodei start un fir va trece ˆ starea ”Runnable”, adic˘ va fi a ın a ˆ executie. ın ¸ fir.start(); //fir se gaseste in starea "Runnable" Metoda start realizez˘ urm˘toarele operatiuni necesare rul˘rii firului de a a ¸ a executie: ¸ • Aloc˘ resursele sistem necesare. a • Planific˘ firul de executie la procesor pentru a fi lansat. a ¸ • Apeleaz˘ metoda run a obiectului activ al firului. a Un fir aflat ˆ starea ”Runnable” nu ˆ ın ınseamn˘ neap˘rat c˘ se g˘¸este a a a as efectiv ˆ executie, adic˘ instructiunile sale sunt interpretate de procesor. ın ¸ a ¸ Acest lucru se ˆ ampl˘ din cauza c˘ majoritatea calculatoarelor au un singur ıntˆ a a procesor iar acesta nu poate rula simultan toate firele de executie care se ¸ gasesc ˆ starea ”Runnable”. Pentru a rezolva aceasta problem˘ exist˘ o ın a a planificare care s˘ partajeze dinamic ¸i corect procesorul ˆ a s ıntre toate firele de executie care sunt ˆ starea ”Runnable”. A¸adar, un fir care ”ruleaz˘” poate ¸ ın s a s˘-¸i a¸tepte de fapt rˆndul la procesor. as s a Starea ”Not Runnable” Un fir de executie poate ajunge ˆ aceaat˘ stare ˆ una din urm˘toarele ¸ ın a ın a situatii: ¸ • Este ”adormit” prin apelul metodei sleep; • A apelat metoda wait, a¸teptˆnd ca o anumit˘ conditie s˘ fie satisfa- s a a ¸ a cut˘; a
  • 355.
    354 CAPITOLUL 12. FIRE DE EXECUTIE ¸ • Este blocat ˆ ıntr-o operatie de intrare/ie¸ire. ¸ s Metoda sleep este o metod˘ static˘ a clasei Thread care provoac˘ o a a a pauz˘ ˆ timpul rul˘rii firului curent aflat ˆ executie, cu alte cuvinte ˆ a ın a ın ¸ ıl ”adoarme” pentru un timp specificat. Lungimea acestei pauze este specificat˘ a ˆ milisecunde ¸i chiar nanosecunde. Intrucˆt poate provoca exceptii de tipul ın s a ¸ InterruptedException, apelul acestei metode se face ˆ ıntr-un bloc de tip try-cacth: try { // Facem pauza de o secunda Thread.sleep(1000); } catch (InterruptedException e) { ... } Observati c˘ metoda fiind static˘ apelul ei nu se face pentru o instant˘ anume ¸ a a ¸a a clasei Thread. Acest lucru este foarte normal deoarece, la un moment dat, un singur fir este ˆ executie ¸i doar pentru acesta are sens ”adormirea” sa. ın ¸ s In intervalul ˆ care un fir de executie ”doarme”, acesta nu va fi execut ın ¸ chiar dac˘ procesorul devine disponibil. Dup˘ expirarea intervalului specifi- a a cat firul revine ˆ starea ”Runnable” iar dac˘ procesorul este ˆ continuare ın a ın disponibil ˆ va continua executia. ısi ¸ Pentru fiecare tip de intrare ˆ starea ”Not Runnable”, exist˘ o secvent˘ ın a ¸a specific˘ de ie¸ire din starea repectiv˘, care readuce firul de executie ˆ starea a s a ¸ ın ”Runnable”. Acestea sunt: • Dac˘ un fir de executie a fost ”adormit”, atunci el devine ”Runnable” a ¸ doar dup˘ scurgerea intervalului de timp specificat de instructiunea a ¸ sleep. • Dac˘ un fir de executie a¸teapt˘ o anumit˘ conditie, atunci un alt a ¸ s a a ¸ obiect trebuie s˘ ˆ informeze dac˘ acea conditie este ˆ a ıl a ¸ ındeplinit˘ sau a nu; acest lucru se realizeaz˘ prin instructiunile notify sau notifyAll a ¸ (vezi ”Sincronizarea firelor de executie”). ¸ • Dac˘ un fir de executie este blocat ˆ a ¸ ıntr-o operatiune de intrare/ie¸ire ¸ s atunci el redevine ”Runnable” atunci cˆnd acea operatiune s-a termi- a ¸ nat.
  • 356.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 355 Starea ”Dead” Este starea ˆ care ajunge un fir de executie la terminarea sa. Un fir nu ın ¸ poate fi oprit din program printr-o anumit˘ metod˘, ci trebuie s˘ se termine a a a ˆ mod natural la ˆ ın ıncheierea metodei run pe care o execut˘. Spre deosebire de a versiunile curente ale limbajului Java, ˆ versiunile mai vechi exista metoda ın stop a clasei Thread care termina fortat un fir de executie, ˆ a aceasta a ¸ ¸ ıns˘ fost eliminat˘ din motive de securitate. A¸adar, un fir de executie trebuie a s ¸ s˘-¸i ”aranjeze” singur propria sa ”moarte”. as 12.3.1 Terminarea unui fir de executie ¸ Dup˘ cum am vazut, un fir de executie nu poate fi terminat fortat de c˘tre a ¸ ¸ a program ci trebuie s˘-¸i ”aranjeze” singur terminarea sa. Acest lucru poate as fi realizat ˆ dou˘ modalit˘¸i: ın a at 1. Prin scrierea unor metode run care s˘-¸i termine executia ˆ mod nat- as ¸ ın ural. La terminarea metodei run se va termina automat ¸i firul de s executie, acesta intrˆnd ˆ starea Dead. Ambele exemple anteriorare ¸ a ın se ˆ ıncadreaz˘ ˆ aceast˘ categorie. a ın a // Primul exemplu public void run() { for(int i = a; i <= b; i += pas) System.out.print(i + " " ); } Dup˘ afi¸area numerelor din intervalul specificat, metoda se termin˘ a s a ¸i, odat˘ cu ea, se va termina ¸i firul de executie repsectiv. s a s ¸ 2. Prin folosirea unei variabile de terminare. In cazul cˆnd metoda run a trebuie s˘ execute o bucl˘ infinit˘ atunci aceasta trebuie controlat˘ a a a a printr-o variabil˘ care s˘ opreasc˘ ciclul atunci cˆnd dorim ca firul de a a a a executie s˘ se termine. Uzual, vom folosi o variabil˘ membr˘ a clasei ¸ a a a care descrie firul de executie care fie este public˘, fie este asociat˘ cu o ¸ a a metod˘ public˘ care permite schimbarea valorii sale. a a S˘ consider˘m exemplul unui fir de executie care trebuie s˘ numere se- a a ¸ a cundele scurse pˆn˘ la ap˘sarea tastei Enter. a a a
  • 357.
    356 CAPITOLUL 12. FIRE DE EXECUTIE ¸ Listing 12.3: Folosirea unei variabile de terminare import java . io .*; class NumaraSecunde extends Thread { public int sec = 0; // Folosim o variabila de terminare public boolean executie = true ; public void run () { while ( executie ) { try { Thread . sleep (1000) ; sec ++; System . out . print ( " . " ) ; } catch ( Interr u pt ed Ex ce pt io n e ) {} } } } public class TestTerminare { public static void main ( String args []) throws IOException { NumaraSecunde fir = new NumaraSecunde () ; fir . start () ; System . out . println ( " Apasati tasta Enter " ) ; System . in . read () ; // Oprim firul de executie fir . executie = false ; System . out . println ( "S - au scurs " + fir . sec + " secunde " ) ; } } Nu este necesar˘ distrugerea explicit˘ a unui fir de executie. Sistemul a a ¸ Java de colectare a ”gunoiului” se ocup˘ de acest lucru. Setarea valorii a null pentru variabila care referea instanta firului de executie va u¸ura ˆ a ¸ ¸ s ıns˘ activitatea procesului gc. Metoda System.exit va oprit fortat toate firele de executie ¸i va termina ¸ ¸ s aplicatia curent˘. ¸ a
  • 358.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 357 Pentru a testa dac˘ un fir de executie a fost pornit dar nu s-a terminat a ¸ ˆ a putem folosi metoda isAlive. Metoda returneaz˘: ınc˘ a • true - dac˘ firul este ˆ una din st˘rile ”Runnable” sau ”Not Runnable” a ın a • false - dac˘ firul este ˆ una din starile ”New Thread” sau ”Dead” a ın Intre st˘rile ”Runnable” sau ”Not Runnable”, repectiv ”New Thread” a sau ”Dead” nu se poate face nici o diferentiere. ¸ NumaraSecunde fir = new NumaraSecunde(); // isAlive retuneaza false (starea este New Thread) fir.start(); // isAlive retuneaza true (starea este Runnable) fir.executie = false; // isAlive retuneaza false (starea este Dead) 12.3.2 Fire de executie de tip ”daemon” ¸ Un proces este considerat ˆ executie dac˘ contine cel putin un fir de executie ın ¸ a ¸ ¸ ¸ activ. Cu alte cuvinte, la rularea unei aplicatii, ma¸ina virtual˘ Java nu se va ¸ s a opri decˆt atunci cˆnd nu mai exist˘ nici un fir de executie activ. De multe ori a a a ¸ ˆ a dorim s˘ folosim fire care s˘ realizeze diverse activit˘¸i, eventual periodic, ıns˘ a a at pe toat˘ durata de executie a programului iar ˆ momentul termin˘rii acestuia a ¸ ın a s˘ se termine automat ¸i firele respective. Aceste fire de executie se numesc a s ¸ demoni. Dup˘ crearea sa, un fir de executie poate fi f˘cut demon, sau scos din a ¸ a aceast˘ stare, cu metoda setDaemon. a Listing 12.4: Crearea unui fir de excutie de tip ”daemon” ¸ class Beeper implements Runnable { public void run () { while ( true ) { java . awt . Toolkit . getDef aultToo lkit () . beep () ; try { Thread . sleep (1000) ; } catch ( InterruptedEx c e p t i o n e ) {} }
  • 359.
    358 CAPITOLUL 12. FIRE DE EXECUTIE ¸ } } public class TestDaemon { public static void main ( String args []) throws java . io . IOException { Thread t = new Thread ( new Beeper () ) ; t . setDaemon ( true ) ; t . start () ; System . out . println ( " Apasati Enter ... " ) ; System . in . read () ; // " Demonul " se termina automat // la terminarea aplicatiei } } 12.3.3 Stabilirea priorit˘¸ilor de executie at ¸ Majoritatea calculatoarelor au un sigur procesor, ceea ce ˆ ınseamn˘ c˘ firele a a de executie trebuie s˘-¸i ˆ ¸ a s ımpart˘ accesul la acel procesor. Executia ˆ a ¸ ıntr-o an- umit˘ ordine a mai multor fire de executie pe un num˘r limitat de procesoare a ¸ a se nume¸te planificare (scheduling). Sistemul Java de executie a programelor s ¸ implementeaz˘ un algoritm simplu, determinist de planificare, cunoscut sub a numele de planificare cu priorit˘¸i fixate. at Fiecare fir de executie Java prime¸te la crearea sa o anumit˘ priori- ¸ s a tate. O prioritate este de fapt un num˘r ˆ a ıntreg cu valori cuprinse ˆ ıntre MIN PRIORITY ¸i MAX PRIORITY. Implicit, prioritatea unui fir nou creat are s valoarea NORM PRIORITY. Aceste trei constante sunt definite ˆ clasa Thread ın astfel: public static final int MAX_PRIORITY = 10; public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY= 5; Schimbarea ulterioar˘ a priorit˘¸ii unui fir de executie se realizeaz˘ cu metoda a at ¸ a setPriority a clasei Thread. La nivelul sistemului de operare, exist˘ dou˘ modele de lucru cu fire de a a executie: ¸
  • 360.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 359 • Modelul cooperativ, ˆ care firele de executie decid cˆnd s˘ cedeze pro- ın ¸ a a cesorul; dezavantajul acestui model este c˘ unele fire pot acapara pro- a cesorul, nepermitˆnd ¸i executia altora pˆn˘ la terminarea lor. ¸a s ¸ a a • Modelul preemptiv, ˆ care firele de executie pot fi ˆ ın ¸ ıntrerupte oricˆnd, a dup˘ ce au fost l˘sate s˘ ruleze o perioad˘, urmˆnd s˘ fie reluate dup˘ a a a a a a a ce ¸i celelalte fire aflate ˆ executie au avut acces la procesor; acest s ın ¸ sistem se mai nume¸te cu ”cuante de timp”, dezavantajul s˘u fiind s a nevoia de a sincroniza accesul firelor la resursele comune. A¸adar, ˆ modelul cooperativ firele de executie sunt responsabile cu par- s ım ¸ tajarea timpului de executie, ˆ timp ce ˆ modelul preemptiv ele trebuie ¸ ın ın s˘ partajeze resursele comune. Deoarece specificatiile ma¸inii virtuale Java a ¸ s nu impun folosirea unui anumit model, programele Java trebuie scrise astfel ˆ at s˘ functioneze corect pe ambele modele. In continuare, vom mai detalia ıncˆ a ¸ putin aceste aspecte. ¸ Planificatorul Java lucreaz˘ ˆ modul urmator: dac˘ la un moment dat a ın a sunt mai multe fire de executie ˆ starea ”Runnable”, adic˘ sunt pregatite ¸ ın a pentru a fi rulate, planificatorul ˆ va alege pe cel cu prioritatea cea mai ıl mare pentru a-l executa. Doar cˆnd firul de executie cu prioritate maxim˘ se a ¸ a termin˘, sau este suspendat din diverse motive, va fi ales un fir cu o prioritate a mai mic˘. In cazul ˆ care toate firele au aceea¸i prioritate ele sunt alese pe a ın s rˆnd, dup˘ un algoritm simplu de tip ”round-robin”. De asemenea, dac˘ a a a un fir cu prioritate mai mare decˆt firul care se execut˘ la un moment dat a a solicit˘ procesorul, atunci firul cu prioritate mai mare este imediat trecut ˆ a ın executie iar celalalt trecut ˆ asteptare. Planificatorul Java nu va ˆ ¸ ın ıntrerupe ˆ a un fir de executie ˆ favoarea altuia de aceeasi prioritate, ˆ a acest lucru ıns˘ ¸ ın ıns˘ ˆ poate face sistemul de operare ˆ cazul ˆ care acesta aloc˘ procesorul ˆ ıl ın ın a ın cuante de timp (un astfel de SO este Windows). A¸adar, un fir de executie Java cedeaz˘ procesorul ˆ una din situatiile: s ¸ a ın • un fir de executie cu o prioritate mai mare solicit˘ procesorul; ¸ a • metoda sa run se termin˘; a • face explicit acest lucru apelˆnd metoda yield; a • timpul alocat pentru executia s˘ a expirat (pe SO cu cuante de timp). ¸ a
  • 361.
    360 CAPITOLUL 12. FIRE DE EXECUTIE ¸ Atentie ¸ In nici un caz corectitudinea unui program nu trebuie s˘ se bazeze pe a mecansimul de planificare a firelor de executie, deoarece acesta poate fi diferit ¸ de la un sistem de operare la altul. Un fir de executie de lung˘ durat˘ ¸i care nu cedeaz˘ explicit procesorul la ¸ a as a anumite intervale de timp astfel ˆ at s˘ poata fi executate ¸i celelalte fire de ıncˆ a s executie se nume¸te fir de executie egoist. Evident, trebuie evitat˘ scrierea lor ¸ s ¸ a ˆ ıntrucˆt acapareaz˘ pe termen nedefinit procesorul, blocˆnd efectiv executia a a a ¸ celorlalte fire de executie pˆn˘ la terminarea sa. Unele sistemele de oper- ¸ a a are combat acest tip de comportament prin metoda aloc˘rii procesorului ˆ a ın cuante de timp fiec˘rui fir de executie, ˆ a nu trebuie s˘ ne baz˘m pe acest a ¸ ıns˘ a a lucru la scrierea unui program. Un fir de executie trebuie s˘ fie ”corect” ¸ a fat˘de celelalte fire ¸i s˘ cedeze periodic procesorul astfel ˆ at toate s˘ aib˘ ¸a s a ıncˆ a a posibilitatea de a se executa. Listing 12.5: Exemplu de fir de executie ”egoist” ¸ class FirEgoist extends Thread { public FirEgoist ( String name ) { super ( name ) ; } public void run () { int i = 0; while ( i < 100000) { // Bucla care acapareaza procesorul i ++; if ( i % 100 == 0) System . out . println ( getName () + " a ajuns la " + i ) ; // yield () ; } } } public class TestFirEgoist { public static void main ( String args []) { FirEgoist s1 , s2 ; s1 = new FirEgoist ( " Firul 1 " ) ; s1 . setPriority ( Thread . MAX_PRIORITY ) ; s2 = new FirEgoist ( " Firul 2 " ) ; s2 . setPriority ( Thread . MAX_PRIORITY ) ;
  • 362.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 361 s1 . start () ; s2 . start () ; } } Firul de executie s1 are prioritate maxim˘ ¸i pˆn˘ nu-¸i va termina ¸ a s a a s executia nu-i va permite firului s2 s˘ execute nici o instructiune, acaparˆnd ¸ a ¸ a efectiv procesorul. Rezultatul va ar˘ta astfel: a Firul 1 a ajuns la 100 Firul 1 a ajuns la 200 Firul 1 a ajuns la 300 ... Firul 1 a ajuns la 99900 Firul 1 a ajuns la 100000 Firul 2 a ajuns la 100 Firul 2 a ajuns la 200 ... Firul 2 a ajuns la 99900 Firul 2 a ajuns la 100000 Rezolvarea acestei probleme se face fie prin intermediul metodei statice yield a clasei Thread, care determin˘ firul de executie curent s˘ se opreasc˘ tem- a ¸ a a porar, dˆnd ocazia ¸i altor fire s˘ se execute, fie prin ”adormirea” temporar˘ a s a a a firului curent cu ajutorul metodei sleep. Prin metoda yield un fir de executie nu cedeaz˘ procesorul decˆt firelor de executie care au aceea¸i pri- ¸ a a ¸ s oritate cu a sa ¸i nu celor cu priorit˘¸i mai mici. Decomentˆnd linia ˆ care s at a ın apel˘m yeld din exemplul anterior, executia celor dou˘ fire se va intercala. a ¸ a ... Firul 1 a ajuns la 31900 Firul 1 a ajuns la 32000 Firul 2 a ajuns la 100 Firul 1 a ajuns la 32100 Firul 2 a ajuns la 200 Firul 2 a ajuns la 300 ...
  • 363.
    362 CAPITOLUL 12. FIRE DE EXECUTIE ¸ 12.3.4 Sincronizarea firelor de executie ¸ Pˆn˘ acum am v˘zut cum putem crea fire de executie independente ¸i as- a a a ¸ s incrone, cu alte cuvinte care nu depind ˆ nici un fel de executia sau de ın ¸ rezultatele altor fire. Exist˘ ˆ a numeroase situatii cˆnd fire de executie a ıns˘ ¸ a ¸ separate, dar care ruleaz˘ concurent, trebuie s˘ comunice ˆ a a ıntre ele pentru a accesa diferite resurse comune sau pentru a-¸i transmite dinamic rezul- s tatele ”muncii” lor. Cel mai elocvent scenariu ˆ care firele de executie ın ¸ trebuie s˘ se comunice ˆ a ıntre ele este cunoscut sub numele de problema pro- duc˘torului/consumatorului, ˆ care produc˘torul genereaz˘ un flux de date a ın a a care este preluat ¸i prelucrat de c˘tre consumator. s a S˘ consider˘m de exemplu o aplicatie Java ˆ care un fir de executie (pro- a a ¸ ın ¸ duc˘torul) scrie date ˆ a ıntr-un fi¸ier ˆ timp ce alt fir de executie (consuma- s ın ¸ torul) cite¸te date din acela¸i fi¸ier pentru a le prelucra. Sau, s˘ presupunem s s s a c˘ produc˘torul genereaz˘ ni¸te numere ¸i le plaseaz˘, pe rˆnd, ˆ a a a s s a a ıntr-un buffer iar consumatorul cite¸te numerele din acel buffer pentru a le procesa. In am- s bele cazuri avem de-a face cu fire de executie concurente care folosesc o ¸ resurs˘ comun˘: un fi¸ier, respectiv o zon˘ de memorie ¸i, din acest motiv, a a s a s ele trebuie sincronizate ˆ ıntr-o manier˘ care s˘ permit˘ decurgerea normal˘ a a a a a activit˘¸ii lor. at 12.3.5 Scenariul produc˘tor / consumator a Pentru a ˆ ıntelege mai bine modalitatea de sincronizare a dou˘ fire de executie a ¸ s˘ implement˘m efectiv o problem˘ de tip produc˘tor/consumator. S˘ con- a a a a a sider˘m urm˘toarea situatie: a a ¸ • Produc˘torul genereaz˘ numerele ˆ a a ıntregi de la 1 la 10, fiecare la un interval neregulat cuprins ˆ ıntre 0 ¸i 100 de milisecunde. Pe m˘sura ce s a le genereaz˘ ˆ a ıncearc˘ s˘ le plaseze ˆ a a ıntr-o zon˘ de memorie (o variabil˘ a a ˆ ıntreaga) de unde s˘ fie citite de c˘tre consumator. a a • Consumatorul va prelua, pe rˆnd, numerele generate de c˘tre pro- a a duc˘tor ¸i va afi¸a valoarea lor pe ecran. a s s Pentru a fi accesibil˘ ambelor fire de executie, vom ˆ a ¸ ıncapsula variabila ce va contine numerele generate ˆ ıntr-un obiect descris de clasa Buffer ¸i care va s avea dou˘ metode put (pentru punerea unui numar ˆ buffer) ¸i get (pentru a ın s obtinerea numarului din buffer).
  • 364.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 363 F˘r˘ a folosi nici un mecanism de sincronizare clasa Buffer arat˘ astfel: aa a Listing 12.6: Clasa Buffer f˘r˘ sincronizare aa class Buffer { private int number = -1; public int get () { return number ; } public void put ( int number ) { this . number = number ; } } Vom implementa acum clasele Producator ¸i Consumator care vor descrie s cele dou˘ fire de executie. Ambele vor avea o referinta comun˘ la un obiect a ¸ ¸ a de tip Buffer prin intermediul c˘ruia ˆsi comunic˘ valorile. a ı¸ a Listing 12.7: Clasele Producator ¸i Consumator s class Producator extends Thread { private Buffer buffer ; public Producator ( Buffer b ) { buffer = b ; } public void run () { for ( int i = 0; i < 10; i ++) { buffer . put ( i ) ; System . out . println ( " Producatorul a pus : t " + i ) ; try { sleep (( int ) ( Math . random () * 100) ) ; } catch ( InterruptedE x c e p t i o n e ) { } } } } class Consumator extends Thread { private Buffer buffer ; public Consumator ( Buffer b ) { buffer = b ;
  • 365.
    364 CAPITOLUL 12. FIRE DE EXECUTIE ¸ } public void run () { int value = 0; for ( int i = 0; i < 10; i ++) { value = buffer . get () ; System . out . println ( " Consumatorul a primit : t " + value ) ; } } } public class TestSincron izare1 { public static void main ( String [] args ) { Buffer b = new Buffer () ; Producator p1 = new Producator ( b ) ; Consumator c1 = new Consumator ( b ) ; p1 . start () ; c1 . start () ; } } Dup˘ cum ne a¸teptam, rezultatul rul˘rii acestui program nu va rezolva a s a nici pe departe problema propus˘ de noi, motivul fiind lipsa oric˘rei sin- a a croniz˘ri ˆ a ıntre cele dou˘ fire de executie. Mai precis, rezultatul va fi ceva de a ¸ forma: Consumatorul a primit: -1 Consumatorul a primit: -1 Producatorul a pus: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Consumatorul a primit: 0 Producatorul a pus: 1 Producatorul a pus: 2 Producatorul a pus: 3 Producatorul a pus: 4 Producatorul a pus: 5
  • 366.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 365 Producatorul a pus: 6 Producatorul a pus: 7 Producatorul a pus: 8 Producatorul a pus: 9 Ambele fire de executie acceseaz˘ resursa comun˘, adic˘ obiectul de tip ¸ a a a Buffer, ˆ ıntr-o manier˘ haotic˘ ¸i acest lucru se ˆ ampla din dou motive : a as ıntˆ • Consumatorul nu a¸teapt˘ ˆ s a ınainte de a citi ca produc˘torul s˘ genereze a a un num˘r ¸i va prelua de mai multe ori acela¸i num˘r. a s s a • Produc˘torul nu a¸teapt˘ consumatorul s˘ preia num˘rul generat ˆ a s a a a ınainte de a produce un altul, ˆ felul acesta consumatorul va ”rata” cu sigurant˘ ın ¸a unele numere (ˆ cazul nostru aproape pe toate). ın Problema care se ridic˘ ˆ acest moment este: cine trebuie s˘ se ocupe de a ın a sincronizarea celor dou˘ fire de executie : clasele Producator ¸i Consumator a ¸ s sau resursa comuna Buffer ? R˘spunsul este evident: resursa comun˘ a a Buffer, deoarece ea trebuie s˘ permita sau nu accesul la continutul s˘u ¸i a ¸ a s nu firele de executie care o folosesc. In felul acesta efortul sincroniz˘rii este ¸ a transferat de la produc˘tor/consumator la un nivel mai jos, cel al resursei a critice. Activit˘¸ile produc˘torului ¸i consumatorului trebuie sincronizate la nivelul at a s resursei comune ˆ dou˘ privinte: ın a ¸ • Cele dou˘ fire de executie nu trebuie s˘ acceseze simultan buffer-ul; a ¸ a acest lucru se realizeaz˘ prin blocarea obiectului Buffer atunci cˆnd a a este accesat de un fir de executie, astfel ˆ at nici nu alt fir de executie ¸ ıncˆ ¸ s˘ nu-l mai poat˘ accesa (vezi ”Monitoare”). a a • Cele dou˘ fire de executie trebuie s˘ se coordoneze, adic˘ produc˘torul a ¸ a a a trebuie s˘ g˘seasc˘ o modalitate de a ”spune” consumatorului c˘ a a a a a plasat o valoare ˆ buffer, iar consumatorul trebuie s˘ comunice pro- ın a duc˘torului c˘ a preluat aceast˘ valoare, pentru ca acesta s˘ poat˘ gen- a a a a a era o alta. Pentru a realiza aceasta comunicare, clasa Thread pune la dispozitie metodele wait, notify, notifyAll. (vezi ”Semafoare”). ¸ Folosind sincronizarea clasa Buffer va ar˘ta astfel: a
  • 367.
    366 CAPITOLUL 12. FIRE DE EXECUTIE ¸ Listing 12.8: Clasa Buffer cu sincronizare class Buffer { private int number = -1; private boolean available = false ; public synchronized int get () { while (! available ) { try { wait () ; // Asteapta producatorul sa puna o valoare } catch ( Inter r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } available = false ; notifyAll () ; return number ; } public synchronized void put ( int number ) { while ( available ) { try { wait () ; // Asteapta consumatorul sa preia valoarea } catch ( Inter r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } this . number = number ; available = true ; notifyAll () ; } } Rezultatul obtinut va fi cel scontat: Producatorul a pus: 0 Consumatorul a primit: 0 Producatorul a pus: 1 Consumatorul a primit: 1 ... Producatorul a pus: 9 Consumatorul a primit: 9
  • 368.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 367 12.3.6 Monitoare Definitie¸ Un segment de cod ce gestioneaz˘ o resurs˘ comun˘ mai multor de fire de a a a executie separate concurente se nume¸te sectiune critic˘. In Java, o sectiune ¸ s ¸ a ¸ critic˘ poate fi un bloc de instructiuni sau o metod˘. a ¸ a Controlul accesului ˆıntr-o sectiune critic˘ se face prin cuvˆntul cheie syn- ¸ a a chronized. Platforma Java asociaz˘ un monitor (”lac˘t”) fiec˘rui obiect al a a a unui program aflat ˆ executie. Acest monitor va indica dac˘ resursa critic˘ ın ¸ a a este accesat˘ de vreun fir de executie sau este liber˘, cu alte cuvinte ”mon- a ¸ a itorizeaz˘” resursa respectiv˘. In cazul ˆ care este accesat˘, va pune un a a ın a lac˘t pe aceasta, astfel ˆ at s˘ ˆ a ıncˆ a ımpiedice accesul altor fire de executie la ea. ¸ In momentul cˆnd resursa este eliberat˘ ”lac˘tul” va fi eliminat, pentru a a a a permite accesul altor fire de executie.¸ In exemplul de tip produc˘tor/consumator de mai sus, sectiunile critice a ¸ sunt metodele put ¸i get iar resursa critic˘ comun˘ este obiectul buffer. s a a Consumatorul nu trebuie s˘ acceseze buffer-ul cˆnd producatorul tocmai a a pune o valoare ˆ el, iar produc˘torul nu trebuie s˘ modifice valoarea din ın a a buffer ˆ momentul cˆnd aceasta este citit˘ de c˘tre consumator. ın a a a public synchronized int get() { ... } public synchronized void put(int number) { ... } S˘ observam c˘ ambele metode au fost declarate cu modificatorul synchronized. a a Cu toate acestea, sistemul asociaz˘ un monitor unei instante a clasei Buffer a ¸ ¸i nu unei metode anume. In momentul ˆ care este apelat˘ o metod˘ sin- s ın a a cronizat˘, firul de executie care a facut apelul va bloca obiectul a c˘rei metod˘ a ¸ a a o acceseaz˘, ceea ce ˆ a ınseamn˘ c˘ celelalte fire de executie nu vor mai putea a a ¸ accesa resursele critice ale acelui obiect. Acesta este un lucru logic, deoarece mai multe sectiuni critice ale unui obiect gestioneaz˘ de fapt o singur˘ resurs˘ ¸ a a a critic˘. a In exemplul nostru, atunci cˆnd producatorul apeleaz˘ metoda put pen- a a tru a scrie un num˘r, va bloca tot obiectul buffer, astfel c˘ firul de executie a a ¸
  • 369.
    368 CAPITOLUL 12. FIRE DE EXECUTIE ¸ consumator nu va avea acces la metoda get, ¸i reciproc. s public synchronized void put(int number) { // buffer blocat de producator ... // buffer deblocat de producator } public synchronized int get() { // buffer blocat de consumator ... // buffer deblocat de consumator } Monitoare fine Adeseori, folosirea unui monitor pentru ˆıntreg obiectul poate fi prea restric- tiv˘. De ce s˘ bloc˘m toate resursele unui obiect dac˘ un fir de executie a a a a ¸ nu dore¸te decˆt accesarea uneia sau a cˆtorva dintre ele ? Deoarece orice s a a obiect are un monitor, putem folosi obiecte fictive ca lac˘te pentru fiecare a din resursele obiectului nostru, ca ˆ exemplul de mai jos: ın class MonitoareFine { //Cele doua resurse ale obiectului Resursa x, y; //Folosim monitoarele a doua obiecte fictive Object xLacat = new Object(), yLacat = new Object(); public void metoda() { synchronized(xLacat) { // Accesam resursa x } // Cod care nu foloseste resursele comune ... synchronized(yLacat) { // Accesam resursa y }
  • 370.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 369 ... synchronized(xLacat) { synchronized(yLacat) { // Accesam x si y } } ... synchronized(this) { // Accesam x si y } } } Metoda de mai sus nu a fost declarat˘ cu synchronized ceea ce ar fi de- a terminat blocarea tuturor resurselor comune la accesarea obiectului respectiv de un fir de executie, ci au fost folosite monitoarele unor obiecte fictive pentru ¸ a controla folosirea fiec˘rei resurs˘ ˆ parte. a a ın 12.3.7 Semafoare Obiectul de tip Buffer din exemplul anterior are o variabil˘ membr˘ privat˘ a a a numit˘ number, ˆ care este memorat num˘rul pe care ˆ comunic˘ produ- a ın a ıl a catorul ¸i pe care ˆ preia consumatorul. De asemenea, mai are o variabil˘ s ıl a privat˘ logic˘ available care ne d˘ starea buffer-ului: dac˘ are valoarea true a a a a ˆ ınseamn˘ c˘ produc˘torul a pus o valoare ˆ buffer ¸i consumatorul nu a a a a ın s preluat-o ˆ ınca; dac˘ este false, consumatorul a preluat valoarea din buffer a dar produc˘torul nu a pus deocamdat˘ alta la loc. Deci, la prima vedere, a a metodele clasei Buffer ar trebui s˘ arate astfel: a public synchronized int get() { while (!available) { // Nimic - asteptam ca variabila sa devina true } available = false; return number; } public synchronized int put(int number) { while (available) {
  • 371.
    370 CAPITOLUL 12. FIRE DE EXECUTIE ¸ // Nimic - asteptam ca variabila sa devina false } available = true; this.number = number; } Varianta de mai sus, de¸i pare corect˘, nu este. Aceasta deoarece im- s a plementarea metodelor este ”selfish”, cele dou˘ metode ˆ asteapt˘ ˆ mod a ısi a ın egoist conditia de terminare. Ca urmare, corectitudinea function˘rii va de- ¸ ¸ a pinde de sistemul de operare pe care programul este rulat, ceea ce reprezint˘ a o gre¸eal˘ de programare. s a Punerea corect˘ a unui fir de executie ˆ asteptare se realizeaz˘ cu metoda a ¸ ın a wait a clasei Thread, care are urm˘toarele forme: a void wait( ) void wait( long timeout ) void wait( long timeout, long nanos ) Dup˘ apelul metodei wait, firul de executie curent elibereaz˘ monitorul a ¸ a asociat obiectului respectiv ¸i a¸teapt˘ ca una din urm˘toarele conditii s˘ fie s s a a ¸ a ˆ ındeplinit˘: a • Un alt fir de executie informeaz˘ pe cei care ”a¸teapt˘” la un anumit ¸ a s a monitor s˘ se ”trezeasc˘” - acest lucru se realizeaz˘ printr-un apel al a a a metodei notifyAll sau notify. • Perioada de a¸tepatare specificat˘ a expirat. s a Metoda wait poate produce exceptii de tipul InterruptedException, ¸ atunci cˆnd firul de executie care a¸teapt˘ (este deci ˆ starea ”Not Runnable”) a ¸ s a ın este ˆ ıntrerupt din a¸teptare ¸i trecut fortat ˆ starea ”Runnable”, de¸i conditia s s ¸ ın s ¸ a¸teptat˘ nu era ˆ a ˆ s a ınc˘ ındeplinit˘. a Metoda notifyAll informeaz˘ toate firele de executie care sunt ˆ asteptare a ¸ ın la monitorul obiectului curent ˆ ındeplinirea conditiei pe care o a¸teptau. Metoda ¸ s notify informeaz˘ doar un singur fir de executie, specificat ca argument. a ¸ Reamintim varianta corect˘ a clasei Buffer: a Listing 12.9: Folosirea semafoarelor class Buffer { private int number = -1;
  • 372.
    ¸˘ 12.3. CICLUL DEVIATA AL UNUI FIR DE EXECUTIE ¸ 371 private boolean available = false ; public synchronized int get () { while (! available ) { try { wait () ; // Asteapta producatorul sa puna o valoare } catch ( InterruptedE x ce pt io n e ) { e . printStackTrace () ; } } available = false ; notifyAll () ; return number ; } public synchronized void put ( int number ) { while ( available ) { try { wait () ; // Asteapta consumatorul sa preia valoarea } catch ( InterruptedE x c e p t i o n e ) { e . printStackTrace () ; } } this . number = number ; available = true ; notifyAll () ; } } 12.3.8 Probleme legate de sincronizare Din p˘cate, folosirea monitoarelor ridic˘ ¸i unele probleme. S˘ analiz˘m a a s a a cˆteva dintre ele ¸i posibilele lor solutii: a s ¸ Deadlock Deadlock-ul este o problem˘ clasic˘ ˆ a a ıntr-un mediu ˆ care ruleaz˘ mai multe ın a fire de executie ¸i const˘ ˆ faptul c˘, la un moment dat, ˆ ¸ s a ın a ıntreg procesul se poate bloca deoarece unele fire a¸teapt˘ deblocarea unor monitoare care s a nu se vor debloca niciodat˘. Exist˘ numeroase exemple ˆ acest sens, cea a a ın mai cunoscut˘ fiind ”Problema filozofilor”. Reformulat˘, s˘ ne imagin˘m a a a a dou˘ persoane ”A” ¸i ”B” (fire de executie) care stau la aceea¸i mas˘ ¸i tre- a s ¸ s as
  • 373.
    372 CAPITOLUL 12. FIRE DE EXECUTIE ¸ buie s˘ foloseasc˘ ˆ comun cutitul ¸i furculita (resursele comune) pentru a a a ın ¸ s ¸ mˆnca. Evident, cele dou˘ persoane doresc obtinerea ambelor resurse. S˘ a a ¸ a presupunem c˘ ”A” a otinut cutitul ¸i ”B” furculita. Firul ”A” se va bloca ˆ a ¸ ¸ s ¸ ın a¸teptarea eliber˘rii furculitei iar firul ”A” se va bloca ˆ a¸tepatrea eliber˘rii s a ¸ ın s a cutitului, ceea ce conduce la starea de ”deadlock”. De¸i acest exemplu este ¸ s desprins de realitate, exist˘ numeroase situatii ˆ care fenomenul de ”dead- a ¸ ın lock” se poate manifesta, multe dintre acestea fiind dificil de detectat. Exist˘ cˆteva reguli ce pot fi aplicate pentru evitarea deadlock-ului: a a • Firele de executie s˘ solicite resursele ˆ aceea¸i ordine. Aceast˘ abor- ¸ a ın s a dare elimin˘ situatiile de a¸teptare circular˘. a ¸ s a • Folosirea unor monitoare care s˘ controleze accesul la un grup de resurse. a In cazul nostru, putem folosi un monitor ”tacˆmuri” care trebuie blocat a ˆ ınainte de a cere furculita sau cutitul. ¸ ¸ • Folosirea unor variabile care s˘ informeze disponibilitatea resurselor a f˘r˘ a bloca monitoarele asociate acestora. aa • Cel mai importat, conceperea unei arhitecturi a sistemului care s˘ evite a pe cˆt posibil aparitia unor potentiale situatii de deaslock. a ¸ ¸ ¸ Variabile volatile Cuvˆntul cheie volatile a fost introdus pentru a controla unele aspecte a legate de optimiz˘rile efectuate de unele compilatoare. S˘ consider˘m urm˘torul a a a a exemplu: class TestVolatile { boolean test; public void metoda() { test = false; // * if (test) { // Aici se poate ajunge... } } }
  • 374.
    12.4. GRUPAREA FIRELORDE EXECUTIE ¸ 373 Un compilator care optimizeaz˘ codul, poate decide c˘ variabila test fi- a a ind setat˘ pe false, corpul if -ului nu se va executa ¸i s˘ exclud˘ secventa a s a a ¸ respectiv˘ din rezultatul compil˘rii. Dac˘ aceast˘ clas˘ ar fi ˆ a accesat˘ a a a a a ıns˘ a de mai multe fire de executie, variabile test ar putea fi setat˘ pe true de un ¸ a alt fir, exact ˆ ıntre instructiunile de atribuire ¸i if ale firului curent. ¸ s Declararea unei variabile cu modificatorul volatile informeaz˘ compila- a torul s˘ nu optimizeze codul ˆ care aceasta apare, previzionˆnd valoarea pe a ın a care variabila o are la un moment dat. Fire de executie inaccesibile ¸ Uneori firele de executie sunt blocate din alte motive decˆt a¸teptarea la ¸ a s un monitor, cea mai frecvent˘ situatie de acest tip fiind operatiunile de in- a ¸ ¸ trare/ie¸ire (IO) blocante. Cˆnd acest lucru se ˆ ampl˘ celelalte fire de s a ınt˘ a executie trebuie s˘ poat˘ accesa ˆ continuare obiectul. Dar dac˘ operatiunea ¸ a a ın a ¸ IO a fost f˘cut˘ ˆ a a ıntr-o metod˘ sincronizat˘, acest lucru nu mai este posibil, a a monitorul obiectului fiind blocat de firul care a¸teapt˘ de fapt s˘ realizeze s a a operatia de intrare/ie¸ire. Din acest motiv, operatiile IO nu trebuie f˘cute ¸ s ¸ a ˆ metode sincronizate. ın 12.4 Gruparea firelor de executie ¸ Gruparea firelor de executie pune la dispozitie un mecanism pentru manipu- ¸ ¸ larea acestora ca un tot ¸i nu individual. De exemplu, putem s˘ pornim sau s˘ s a a suspend˘m toate firele dintr-un grup cu un singur apel de metod˘. Gruparea a a firelor de executie se realizeaz˘ prin intermediul clasei ThreadGroup. ¸ a Fiecare fir de executie Java este membru al unui grup, indiferent dac˘ ¸ a specific˘m explicit sau nu acest lucru. Afilierea unui fir la un anumit grup a se realizeaz˘ la crearea sa ¸i devine permanent˘, ˆ sensul c˘ nu vom putea a s a ın a muta un fir dintr-un grup ˆ altul, dup˘ ce acesta a fost creat. In cazul ˆ ın a ın care cre˘m un fir folosind un constructor care nu specific˘ din ce grup face a a parte, el va fi plasat automat ˆ acela¸i grup cu firul de executie care l-a ın s ¸ creat. La pornirea unui program Java se creeaz˘ automat un obiect de tip a ThreadGroup cu numele main, care va reprezenta grupul tuturor firelor de executie create direct din program ¸i care nu au fost ata¸ate explicit altui ¸ s s grup. Cu alte cuvinte, putem s˘ ignor˘m complet plasarea firelor de executie a a ¸ ˆ grupuri ¸i s˘ l˘s˘m sistemul s˘ se ocupe cu aceasta, adunˆndu-le pe toate ın s a aa a a
  • 375.
    374 CAPITOLUL 12. FIRE DE EXECUTIE ¸ ˆ grupul main. ın Exist˘ situatii ˆ a cˆnd gruparea firelor de executie poate u¸ura substantial a ¸ ıns˘ a ¸ s ¸ manevrarea lor. Crearea unui fir de executie ¸i plasarea lui ˆ ¸ s ıntr-un grup (altul decˆt cel implicit) se realizeaz˘ prin urm˘torii constructori ai clasei Thread: a a a public Thread(ThreadGroup group, Runnable target) public Thread(ThreadGroup group, String name) public Thread(ThreadGroup group, Runnable target, String name) Fiecare din ace¸ti costructori creeaz˘ un fir de executie, ˆ initializeaz˘ ¸i s a ¸ ıl ¸ a s ˆ plaseaz˘ ˆ ıl a ıntr-un grup specificat ca argument. Pentru a afla c˘rui grup a apartine un anumit fir de executie putem folosi metoda getThreadGroup a ¸ ¸ clasei Thread. In exemplul urm˘tor vor fi create dou˘ grupuri, primul cu a a dou˘ fire de executie iar al doilea cu trei: a ¸ ThreadGroup grup1 = new ThreadGroup("Producatori"); Thread p1 = new Thread(grup1, "Producator 1"); Thread p2 = new Thread(grup1, "Producator 2"); ThreadGroup grup2 = new ThreadGroup("Consumatori"); Thread c1 = new Thread(grup2, "Consumator 1"); Thread c2 = new Thread(grup2, "Consumator 2"); Thread c3 = new Thread(grup2, "Consumator 3"); Un grup poate avea ca p˘rinte un alt grup, ceea ce ˆ a ınseamn˘ c˘ firele de a a executie pot fi plasate ˆ ¸ ıntr-o ierarhie de grupuri, ˆ care r˘d˘cina este grupul ın a a implicit main, ca ˆ figura de mai jos: ın
  • 376.
    12.4. GRUPAREA FIRELORDE EXECUTIE ¸ 375 S˘ consider˘m un exemplu ˆ care list˘m firele de executie active: a a ım a ¸ Listing 12.10: Folosirea clasei ThreadGroup public class TestThreadGroup { static class Dummy implements Runnable { public void run () { while ( true ) Thread . yield () ; } } public static void main ( String args []) { // Cream o fereastra pentru a fi create // automat firele de executie din AWT java . awt . Frame f = new java . awt . Frame ( " Test " ) ; // Cream un fir propriu new Thread ( new Dummy () , " Fir de test " ) . start () ; // Obtinem o referinta la grupul curent Thread firCurent = Thread . currentThread () ; ThreadGroup grupCurent = firCurent . getThreadGroup () ; // Aflam numarul firelor de executie active int n = grupCurent . activeCount () ; // Enumeram firele din grup Thread [] lista = new Thread [ n ]; grupCurent . enumerate ( lista ) ; // Le afisam for ( int i =0; i < n ; i ++) System . out . println ( " Thread # " + i + " = " + lista [ i ]. getName () ) ; } }
  • 377.
    376 CAPITOLUL 12. FIRE DE EXECUTIE ¸ 12.5 Comunicarea prin fluxuri de tip ”pipe” O modalitate deosebit de util˘ prin care dou˘ fire de executie pot comunica a a ¸ este realizat˘ prin intermediul canalelor de comunicatii (pipes). Acestea sunt a implementate prin fluxuri descrise de clasele: • PipedReader, PipedWriter - pentru caractere, respectiv • PipedOutputStream, PipedInputStream - pentru octeti. ¸ Fluxurile ”pipe” de ie¸ire ¸i cele de intrare pot fi conectate pentru a s s efectua transmiterea datelor. Acest lucru se realizeaz˘ uzual prin intemediul a constructorilor: public PipedReader(PipedWriterpw) public PipedWriter(PipedReaderpr) In cazul ˆ care este folosit un constructor f˘r˘ argumente, conectarea unui ın aa flux de intrare cu un flux de ie¸ire se face prin metoda connect: s public void connect(PipedWriterpw) public void connect(PipedReaderpr) Intrucˆt fluxurile care sunt conectate printr-un pipe trebuie s˘ execute a a simultan operatii de scriere/citire, folosirea lor se va face din cadrul unor fire ¸ de executie. ¸ Functionarea obicetelor care instantiaz˘ PipedWriter ¸i PipedReader ¸ ¸ a s este asem˘n˘toare cu a canalelor de comunicare UNIX (pipes). Fiecare cap˘t a a a al unui canal este utilizat dintr-un fir de executie separat. La un cap˘t se ¸ a scriu caractere, la cel˘lalt se citesc. La citire, dac˘ nu sunt date disponibile a a firul de executie se va bloca pˆn˘ ce acestea vor deveni disponibile. Se observ˘ ¸ a a a c˘ acesta este un comportament tipic produc˘tor-consumator asincron, firele a a de executie comunicˆnd printr-un canal. ¸ a Realizarea conexiunii se face astfel: PipedWriter pw1 = new PipedWriter(); PipedReader pr1 = new PipedReader(pw1); // sau PipedReader pr2 = new PipedReader(); PipedWriter pw2 = new PipedWriter(pr2); // sau
  • 378.
    12.5. COMUNICAREA PRINFLUXURI DE TIP ”PIPE” 377 PipedReader pr = new PipedReader(); PipedWriter pw = new PipedWirter(); pr.connect(pw) //echivalent cu pw.connect(pr); Scrierea ¸i citirea pe/de pe canale se realizeaz˘ prin metodele uzuale read ¸i s a s write, ˆ toate formele lor. ın S˘ reconsider˘m acum exemplul produc˘tor/consumator prezentat ante- a a a rior, folosind canale de comunicatie. Produc˘torul trimite datele printr-un ¸ a flux de ie¸ire de tip DataOutputStream c˘tre consumator, care le prime¸te s a s printr-un flux de intrare de tip DataInputStream. Aceste dou˘ fluxuri vor fi a interconectate prin intermediul unor fluxuri de tip ”pipe”. Listing 12.11: Folosirea fluxurilor de tip ”pipe” import java . io .*; class Producator extends Thread { private DataOutputStream out ; public Producator ( DataOutputStream out ) { this . out = out ; } public void run () { for ( int i = 0; i < 10; i ++) { try { out . writeInt ( i ) ; } catch ( IOException e ) { e . printStackTrace () ; } System . out . println ( " Producatorul a pus : t " + i ) ; try { sleep (( int ) ( Math . random () * 100) ) ; } catch ( InterruptedE x c e p t i o n e ) { } } } } class Consumator extends Thread { private DataInputStream in ; public Consumator ( DataInputStream in ) { this . in = in ;
  • 379.
    378 CAPITOLUL 12. FIRE DE EXECUTIE ¸ } public void run () { int value = 0; for ( int i = 0; i < 10; i ++) { try { value = in . readInt () ; } catch ( IOException e ) { e . printStackTrace () ; } System . out . println ( " Consumatorul a primit : t " + value ) ; } } } public class TestPipes { public static void main ( String [] args ) throws IOException { PipedOutputStream pipeOut = new Pipe dOutput Stream () ; PipedInputStream pipeIn = new PipedInputStream ( pipeOut ) ; DataOutputStream out = new DataOutputStream ( pipeOut ) ; DataInputStream in = new DataInputStream ( pipeIn ) ; Producator p1 = new Producator ( out ) ; Consumator c1 = new Consumator ( in ) ; p1 . start () ; c1 . start () ; } } 12.6 Clasele Timer ¸i TimerTask s Clasa Timer ofer˘ o facilitate de a planifica diverse actiuni pentru a fi real- a ¸ izate la un anumit moment de c˘tre un fir de executie ce ruleaz˘ ˆ fundal. a ¸ a ın Actiunile unui obiect de tip Timer sunt implementate ca instante ale clasei ¸ ¸ TimerTask ¸i pot fi programate pentru o singur˘ executie sau pentru executii s a ¸ ¸ repetate la intervale regulate. Pa¸ii care trebuie f˘cuti pentru folosirea unui timer sunt: s a ¸
  • 380.
    12.6. CLASELE TIMERSI TIMERTASK ¸ 379 • Crearea unei subclase Actiune a lui TimerTask ¸i supreadefinirea metodei s run ce va contine actiunea pe care vrem s˘ o planific˘m. Dup˘ cum ¸ ¸ a a a vom vedea, pot fi folosite ¸i clase anonime. s • Crearea unui fir de executie prin instantierea clasei Timer; ¸ ¸ • Crearea unui obiect de tip Actiune; • Planificarea la executie a obiectuluii de tip Actiune, folosind metoda ¸ schedule din clasa Timer; Metodele de planificare pe care le avem la dispozitie au urm˘toarele for- ¸ a mate: schedule(TimerTask task, Date time) schedule(TimerTask task, long delay, long period) schedule(TimerTask task, Date time, long period) scheduleAtFixedRate(TimerTask task, long delay, long period) scheduleAtFixedRate(TimerTask task, Date time, long period) unde, task descrie actiunea ce se va executa, delay reprezint˘ ˆ arzierea fata ¸ a ıntˆ ¸ de momentul curent dup˘ care va ˆ a ıncepe executia, time momentul exact la ¸ care va ˆ ıncepe executia iar period intervalul de timp ˆ ¸ ıntre dou˘ executii. a ¸ Dup˘ cum se observ˘, metodele de planificare se ˆ a a ımpart ˆ dou˘ categorii: ın a • schedule - planificare cu ˆ arziere fix˘: dac˘ dintr-un anumit motiv ıntˆ a a actiunea este ˆ arziat˘, urm˘toarele actiuni vor fi ¸i ele ˆ arziate ˆ ¸ ıntˆ a a ¸ s ıntˆ ın consecint˘; ¸a • scheduleAtFixedRate - planificare cu num˘r fix de rate: dac˘ dintr- a a un anumit motiv actiunea este ˆ arziat˘, urm˘toarele actiuni vor fi ¸ ıntˆ a a ¸ executat˘ mai repede, astfel ˆ at num˘rul total de actiuni dintr-o pe- a ıncˆ a ¸ rioad˘ de timp s˘ fie tot timpul acela¸i; a a s Un timer se va opri natural la terminarea metodei sale run sau poate fi oprit fortat folosind metoda cancel. Dup˘ oprirea sa el nu va mai putea fi ¸ a folosit pentru planificarea altor actiuni. De asemenea, metoda System.exit ¸ va oprit fortat toate firele de executie ¸i va termina aplicatia curent˘. ¸ ¸ s ¸ a
  • 381.
    380 CAPITOLUL 12. FIRE DE EXECUTIE ¸ Listing 12.12: Folosirea claselor Timer ¸i TimerTask s import java . util .*; import java . awt .*; class Atentie extends TimerTask { public void run () { Toolkit . getDefaultToolkit () . beep () ; System . out . print ( " . " ) ; } } class Alarma extends TimerTask { public String mesaj ; public Alarma ( String mesaj ) { this . mesaj = mesaj ; } public void run () { System . out . println ( mesaj ) ; } } public class TestTimer { public static void main ( String args []) { // Setam o actiune repetitiva , cu rata fixa final Timer t1 = new Timer () ; t1 . scheduleAtFix e d Ra t e ( new Atentie () , 0 , 1*1000) ; // Folosim o clasa anonima pentru o alta actiune Timer t2 = new Timer () ; t2 . schedule ( new TimerTask () { public void run () { System . out . println ( "S - au scurs 10 secunde . " ) ; // Oprim primul timer t1 . cancel () ; } } , 10*1000) ; // Setam o actiune pentru ora 22:30 Calendar calendar = Calendar . getInstance () ; calendar . set ( Calendar . HOUR_OF_DAY , 22) ; calendar . set ( Calendar . MINUTE , 30) ; calendar . set ( Calendar . SECOND , 0) ; Date ora = calendar . getTime () ;
  • 382.
    12.6. CLASELE TIMERSI TIMERTASK ¸ 381 Timer t3 = new Timer () ; t3 . schedule ( new Alarma ( " Toti copiii la culcare ! " ) , ora ) ; } }
  • 383.
    382 CAPITOLUL 12. FIRE DE EXECUTIE ¸
  • 384.
    Capitolul 13 Programare ˆretea ın ¸ 13.1 Introducere Programarea ˆ retea implic˘ trimiterea de mesaje ¸i date ˆ ın ¸ a s ıntre aplicatii ce ¸ ruleaz˘ pe calculatoare aflate ˆ a ıntr-o retea local˘ sau conectate la Internet. ¸ a Pachetul care ofer˘ suport pentru scrierea aplicatiilor de retea este java.net. a ¸ Clasele din acest pachet ofer˘ o modalitate facil˘ de programare ˆ retea, f˘r˘ a a ın ¸ aa a fi nevoie de cuno¸tinte prealabile referitoare la comunicarea efectiv˘ ˆ s ¸ a ıntre calculatoare. Cu toate acestea, sunt necesare cˆteva notiuni fundamentale a ¸ referitoare la retele cum ar fi: protocol, adresa IP, port, socket. ¸ Ce este un protocol ? Un protocol reprezint˘ o conventie de reprezentare a datelor folosit˘ ˆ comu- a ¸ a ın nicarea ˆıntre dou˘ calculatoare. Avˆnd ˆ vedere faptul c˘ orice informatie a a ın a ¸ care trebuie trimis˘ prin retea trebuie serializat˘ astfel ˆ at s˘ poat˘ fi trans- a ¸ a ıncˆ a a mis˘ secvential, octet cu octet, c˘tre destinatie, era nevoie de stabilirea unor a ¸ a ¸ conventii (protocoale) care s˘ fie folosite atˆt de calculatorul care trimite ¸ a a datele cˆt ¸i de cel care le prime¸te, pentru a se ”ˆ ¸elege” ˆ a s s ınt ıntre ele. Dou˘ dintre cele mai utilizate protocoale sunt TCP ¸i UDP. a s • TCP (Transport Control Protocol) este un protocol ce furnizeaz˘ un a flux sigur de date ˆ ıntre dou˘ calculatoare aflate ˆ retea. Acest proto- a ın ¸ col asigur˘ stabilirea unei conexiuni permanente ˆ a ıntre cele dou˘ calcu- a latoare pe parcursul comunicatiei. ¸ • UDP (User Datagram Protocol) este un protocol bazat pe pachete inde- 383
  • 385.
    384 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ pendente de date, numite datagrame, trimise de la un calculator c˘tre a altul f˘r˘ a se garanta ˆ vreun fel ajungerea acestora la destinatie sau aa ın ¸ ordinea ˆ care acestea ajung. Acest protocol nu stabile¸te o conexiun˘ ın s a permant˘ ˆa ıntre cele dou˘ calculatoare. a Cum este identificat un calculator ˆ retea ?ın ¸ Orice calculator conectat la Internet este identificat ˆ mod unic de adresa sa ın IP (IP este acronimul de la Internet Protocol). Aceasta reprezint˘ un num˘r a a reprezentat pe 32 de biti, uzual sub forma a 4 octeti, cum ar fi de exemplu: ¸ ¸ 193.231.30.131 ¸i este numit adresa IP numeric˘. Corespunz˘toare unei s a a adrese numerice exista ¸i o adresa IP simbolic˘, cum ar fi thor.infoiasi.ro s a pentru adresa numeric˘ anterioar˘. a a De asemenea, fiecare calculator aflat ˆ ıntr-o retea local˘ are un nume unic ¸ a ce poat fi folosit la identificarea local˘ a acestuia. a Clasa Java care reprezint˘ notiunea de adres˘ IP este InetAddress. a ¸ a Ce este un port ? Un calculator are ˆ general o singur˘ leg˘tur˘ fizic˘ la retea. Orice informatie ın a a a a ¸ ¸ destinat˘ unei anumite ma¸ini trebuie deci s˘ specifice obligatoriu adresa a s a IP a acelei ma¸ini. Ins˘ pe un calculator pot exista concurent mai multe s a procese care au stabilite conexiuni ˆ retea, asteptˆnd diverse informatii. ın ¸ a ¸ Prin urmare, datele trimise c˘tre o destinatie trebuie s˘ specifice pe lˆnga a ¸ a a adresa IP a calculatorului ¸i procesul c˘tre care se ˆ s a ındreapt˘ informatiile a ¸ respective. Identificarea proceselor se realizeaz˘ prin intermdiul porturilor. a Un port este un num˘r pe 16 biti care identific˘ ˆ mod unic procesele a ¸ a ın care ruleaz˘ pe o anumit˘ masin˘. Orice aplicatie care realizeaz˘ o conexiune a a a ¸ a ˆ retea va trebui s˘ ata¸eze un num˘r de port acelei conexiuni. Valorile pe ın ¸ a s a care le poate lua un num˘r de port sunt cuprinse ˆ a ıntre 0 ¸i 65535 (deoarece s sunt numere reprezentate pe 16 biti), numerele cuprinse ˆ ¸ ıntre 0 ¸i 1023 fiind s ˆ a rezervate unor servicii sistem ¸i, din acest motiv, nu trebuie folosite ˆ ıns˘ s ın aplicatii. ¸ Clase de baz˘ din java.net a Clasele din java.net permit comunicarea ˆ ıntre procese folosind protocoalele
  • 386.
    13.2. LUCRUL CUURL-URI 385 TCP ¸i UDP ¸i sunt prezentate ˆ tabelul de mai jos. s s ın TCP UDP URL DatagramPacket URLConnection DatagramSocket Socket MulticastSocket ServerSocket 13.2 Lucrul cu URL-uri Termenul URL este acronimul pentru Uniform Resource Locator ¸i reprezint˘ s a o referint˘ (adres˘) la o resurs˘ aflat˘ pe Internet. Aceasta este ˆ general un ¸a a a a ın fi¸ier reprezentˆnd o pagin˘ Web, un text, imagine, etc., ˆ a un URL poate s a a ıns˘ referi ¸i interog˘ri la baze de date, rezultate ale unor comenzi executate la s a distanta, etc. Mai jost, sunt prezentate cˆteva exemple de URL-uri sunt: ¸˘ a http://java.sun.com http://students.infoiasi.ro/index.html http://www.infoiasi.ro/~acf/imgs/taz.gif http://www.infoiasi.ro/~acf/java/curs/9/prog_retea.html#url Dup˘ cum se observ˘ din exemplele de mai sus, un URL are dou˘ com- a a a ponente principale: • Identificatorul protocolului folosit (http, ftp, etc); • Numele resursei referite. Acesta are urm˘toarele componente: a – Numele calculatorului gazd˘ (www.infoiasi.ro). a – Calea complet˘ spre resursa referit˘ ( acf/java/curs/9/prog retea.html). a a Notatia user semnific˘ uzual subdirectorul html al directoru- ¸ a lui rezervat pe un server Web utilizatorului specificat (HOME). In cazul ˆ care este specificat doar un director, fi¸ierul ce reprezint˘ ın s a resursa va fi considerat implicit index.html. – Optional, o referint˘ de tip anchor ˆ cadrul fi¸ierului referit (#url). ¸ ¸a ın s – Optional, portul la care s˘ se realizeze conexiunea. ¸ a
  • 387.
    386 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ Clasa care permite lucrul cu URL-uri este java.net.URL. Aceasta are mai multi constructori pentru crearea de obiecte ce reprezint˘ referinte c˘tre ¸ a ¸ a resurse aflate ˆ retea, cel mai uzual fiind cel care prime¸te ca parametru ın ¸ s un ¸ir de caractere. In cazul ˆ care ¸irul nu reprezint˘ un URL valid va fi s ın s a aruncat˘ o exceptie de tipul MalformedURLException. a ¸ try { URL adresa = new URL("http://xyz.abc"); } catch (MalformedURLException e) { System.err.println("URL invalid !n" + e); } Un obiect de tip URL poate fi folosit pentru: • Aflarea informatiilor despre resursa referit˘ (numele calculatorului gazd˘, ¸ a a numele fi¸ierului, protocolul folosit. etc). s • Citirea printr-un flux a continutului fi¸ierului respectiv. s • Conectarea la acel URL pentru citirea ¸i scrierea de informatii. s ¸ Citirea continutului unui URL ¸ Orice obiect de tip URL poate returna un flux de intrare de tip InputStream pentru citirea continutului s˘u. Secventa standard pentru aceast˘ operatiune a ¸ a este prezentat˘ ˆ exemplul de mai jos, ˆ care afi¸am continutul resursei a ın ın s ¸ specificat˘ la linia de comand˘. Dac˘ nu se specific˘ mici un argument, va fi a a a a afi¸at fi¸ierul index.html de la adresa: http://www.infoiasi.ro. s s Listing 13.1: Citirea continutului unui URL ¸ import java . net .*; import java . io .*; public class CitireURL { public static void main ( String [] args ) throws IOException { String adresa = " http :// www . infoiasi . ro " ; if ( args . length > 0) adresa = args [0];
  • 388.
    13.3. SOCKET-URI 387 BufferedReader br = null ; try { URL url = new URL ( adresa ) ; InputStream in = url . openStream () ; br = new BufferedReader ( new Inpu tStream Reader ( in ) ) ; String linie ; while (( linie = br . readLine () ) != null ) { // Afisam linia citita System . out . println ( linie ) ; } } catch ( Malfo rmedU RLEx c e p t i o n e ) { System . err . println ( " URL invalid ! n " + e ) ; } finally { br . close () ; } } } Conectarea la un URL Se realizeaz˘ prin metoda openConnection ce stabile¸te o conexiune bidirectional˘ a s ¸ a cu resursa specificat˘. Aceast˘ conexiune este reprezentat˘ de un obiect de a a a tip URLConnection, ce permite crearea atˆt a unui flux de intrare pentru a citirea informatiilor de la URL-ul specificat, cˆt ¸i a unui flux de ie¸ire pentru ¸ a s s scrierea de date c˘tre acel URL. Operatiunea de trimitere de date dintr-un a ¸ program c˘tre un URL este similar˘ cu trimiterea de date dintr-un formular a a de tip FORM aflat ˆ ıntr-o pagin˘ HTML. Metoda folosit˘ pentru trimitere este a a POST. In cazul trimiterii de date, obiectul URL este uzual un proces ce ruleaz˘ a pe serverul Web referit prin URL-ul respectiv (jsp, servlet, cgi-bin, php, etc). 13.3 Socket-uri Definitie¸ Un socket (soclu) este o abstractiune software folosit˘ pentru a reprezenta ¸ a fiecare din cele dou˘ ”capete” ale unei conexiuni ˆ a ıntre dou˘ procese ce ruleaz˘ a a ˆ ıntr-o retea. Fiecare socket este ata¸at unui port astfel ˆ at s˘ poat˘ iden- ¸ s ıncˆ a a tifica unic programul c˘ruia ˆ sunt destinate datele. a ıi
  • 389.
    388 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ Socket-urile sunt de dou˘ tipuri: a • TCP, implementate de clasele Socket ¸i ServerSocket; s • UDP, implementate de clasa DatagramSocket. O aplicatie de retea ce folose¸te socket-uri se ˆ ¸ ¸ s ıncadreaz˘ ˆ modelul clien- a ın t/server de concepere a unei aplicatii. In acest model aplicatia este format˘ ¸ ¸ a din dou˘ categorii distincte de programe numite servere, respectiv clienti. a ¸ Programele de tip server sunt cele care ofer˘ diverse servicii eventualilor a clienti, fiind ˆ stare de a¸teptare atˆta vreme cˆt nici un client nu le solicit˘ ¸ ın s a a a serviciile. Programele de tip client sunt cele care initiaz˘ conversatia cu un ¸ a ¸ server, solicitˆnd un anumit serviciu. Uzual, un server trebuie s˘ fie capabil s˘ a a a trateze mai multi clienti simultan ¸i, din acest motiv, fiecare cerere adresat˘ ¸ s a serverului va fi tratat˘ ˆ a ıntr-un fir de executie separat. ¸ Incepˆnd cu versiunea 1.4 a platformei standard Java, exist˘ o clas˘ utili- a a a tar˘ care implementeaz˘ o pereche de tipul (adresa IP, num˘r port). Aceasta a a a este InetSocketAddress (derivat˘ din SocketAddress), obiectele sale fiind a utilizate de constructori ¸i metode definite ˆ cadrul claselor ce descriu sock- s ın eturi, pentru a specifica cei doi parametri necesari identific˘rii unui proces a care trimite sau receptioneaz˘ date ˆ retea. ¸ a ın ¸ 13.4 Comunicarea prin conexiuni In acest model se stabile¸te o conexiune TCP ˆ s ıntre o aplicatie client ¸i o ¸ s aplicatie server care furnizeaz˘ un anumit serviciu. Avantajul protocolul ¸ a TCP/IP este c˘ asigur˘ realizarea unei comunic˘ri stabile, permanente ˆ a a a ın retea, existˆnd siguranta c˘ informatiile trimise de un proces vor fi receptionate ¸ a ¸ a ¸ ¸ corect ¸i complet la destinatie sau va fi semnalat˘ o exceptie ˆ caz contrar. s ¸ a ¸ ın Leg˘tura ˆ a ıntre un client ¸i un server se realizeaz˘ prin intermediul a s a dou˘ obiecte de tip Socket, cˆte unul pentru fiecare cap˘t al ”canalului” a a a de comunicatie dintre cei doi. La nivelul clientului crearea socketului se re- ¸ alizeaz˘ specificˆnd adresa IP a serverului ¸i portul la care ruleaz˘ acesta, a a s a constructorul uzual folosit fiind: Socket(InetAddress address, int port)
  • 390.
    13.4. COMUNICAREA PRINCONEXIUNI 389 La nivelul serverului, acesta trebuie s˘ creeze ˆ ai un obiect de tip a ıntˆ ServerSocket. Acest tip de socket nu asigur˘ comunicarea efectiv˘ cu a a clientii ci este responsabil cu ”ascultarea” retelei ¸i crearea unor obiecte de ¸ ¸ s tip Socket pentru fiecare cerere ap˘rut˘, prin intermediul cˆruia va fi real- a a a izat˘ leg˘tura cu clientul. Crearea unui obiect de tip ServerSocket se face a a specificˆnd portul la care ruleaz˘ serverul, constructorul folosit fiind: a a ServerSocket(int port) Metoda clasei ServerSocket care a¸teapt˘ ”ascult˘” reteaua este accept. s a a ¸ Aceasta blocheaz˘ procesul p˘rinte pˆn˘ la aparitia unui cereri ¸i returneaz˘ a a a a ¸ s a un nou obiect de tip Socket ce va asigura comunicarea cu clientul. Blocarea poate s˘ nu fie permanent˘ ci doar pentru o anumit˘ perioad˘ de timp - a a a a aceasta va fi specificat˘ prin metoda setSoTimeout, cu argumentul dat ˆ a ın milisecunde. Pentru fiecare din cele dou˘ socketuri deschise pot fi create apoi dou˘ a a fluxuri pe octeti pentru citirea, respectiv scrierea datelor. Acest lucru se re- ¸ alizeaz˘ prin intermediul metodelor getInputStream, respectuv getOut- a putStream. Fluxurile obtinute vor fi folosite ˆ ¸ ımpreun˘ cu fluxuri de proce- a sare care s˘ asigure o comunicare facil˘ ˆ a a ıntre cele dou˘ procese. In functie a ¸ de specificul aplicatiei acestea pot fi perechile: ¸ • BufferedReader, BufferedWriter ¸i PrintWriter - pentru comuni- s care prin intermediul ¸irurilor de caractere; s • DataInputStream, DataOutputStream - pentru comunicare prin date primitive; • ObjectInputStream, ObjectOutputStream - pentru cominicare prin intermediul obiectelor;
  • 391.
    390 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ Structura general˘ a unui server bazat pe conexiuni este: a 1. Creeaza un obiect de tip ServerSocket la un anumit port while (true) { 2. Asteapta realizarea unei conexiuni cu un client, folosind metoda accept; (va fi creat un obiect nou de tip Socket) 3. Trateaza cererea venita de la client: 3.1 Deschide un flux de intrare si primeste cererea 3.2 Deschide un flux de iesire si trimite raspunsul 3.3 Inchide fluxurile si socketul nou creat } Este recomandat ca tratarea cererilor s˘ se realizeze ˆ fire de executie a ın ¸ separate, pentru ca metoda accept s˘ poat˘ fi reapelat˘ cˆt mai repede ˆ a a a a ın vederea stabilirii conexiunii cu un alt client. Structura general˘ a unui client bazat pe conexiuni este: a 1. Citeste sau declara adresa IP a serverului si portul la care acesta ruleaza; 2. Creeaza un obiect de tip Socket cu adresa si portul specificate; 3. Comunica cu serverul: 3.1 Deschide un flux de iesire si trimite cererea; 3.2 Deschide un flux de intrare si primeste raspunsul; 3.3 Inchide fluxurile si socketul creat; In exemplul urm˘tor vom implementa o aplicatie client-server folosind a ¸ comunicarea prin conexiuni. Clientul va trimite serverului un nume iar acesta va raspunde prin mesajul ”Hello nume”. Tratarea cererilor se va face ˆ fire ın de executie separate. ¸ Listing 13.2: Structura unui server bazat pe conexiuni import java . net .*; import java . io .*; class ClientThread extends Thread { Socket socket = null ; public ClientThread ( Socket socket ) { this . socket = socket ;
  • 392.
    13.4. COMUNICAREA PRINCONEXIUNI 391 } public void run () { // Executam solicitarea clientului String cerere , raspuns ; try { // in este fluxul de intrare de la client BufferedReader in = new BufferedReader ( new InputStreamReader ( socket . getInputStream () ) ) ; // out este flux de iesire catre client PrintWriter out = new PrintWriter ( socket . getOutputStream () ) ; // Primim cerere de la client cerere = in . readLine () ; // Trimitem raspuns clientului raspuns = " Hello " + cerere + " ! " ; out . println ( raspuns ) ; out . flush () ; } catch ( IOException e ) { System . err . println ( " Eroare IO n " + e ) ; } finally { // Inchidem socketul deschis pentru clientul curent try { socket . close () ; } catch ( IOException e ) { System . err . println ( " Socketul nu poate fi inchis n " + e); } } } } public class SimpleServer { // Definim portul pe care se gaseste serverul // ( in afara intervalului 1 -1024) public static final int PORT = 8100; public SimpleServer () throws IOException { ServerSocket serverSocket = null ;
  • 393.
    392 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ try { serverSocket = new ServerSocket ( PORT ) ; while ( true ) { System . out . println ( " Asteptam un client ... " ) ; Socket socket = serverSocket . accept () ; // Executam solicitarea clientului intr - un fir de executie ClientThread t = new ClientThread ( socket ) ; t . start () ; } } catch ( IOException e ) { System . err . println ( " Eroare IO n " + e ) ; } finally { serverSocket . close () ; } } public static void main ( String [] args ) throws IOException { SimpleServer server = new SimpleServer () ; } } Listing 13.3: Structura unui client bazat pe conexiuni import java . net .*; import java . io .*; public class SimpleClient { public static void main ( String [] args ) throws IOException { // Adresa IP a serverului String adresaServer = " 127.0.0.1 " ; // Portul la care serverul ofera serviciul int PORT = 8100; Socket socket = null ; PrintWriter out = null ; BufferedReader in = null ; String cerere , raspuns ; try { socket = new Socket ( adresaServer , PORT ) ;
  • 394.
    13.5. COMUNICAREA PRINDATAGRAME 393 out = new PrintWriter ( socket . getOutputStream () , true ) ; in = new BufferedReader ( new Inpu tStream Reader ( socket . getInputStream () ) ) ; // Trimitem o cerere la server cerere = " Duke " ; out . println ( cerere ) ; // Asteaptam raspunsul de la server (" Hello Duke !") raspuns = in . readLine () ; System . out . println ( raspuns ) ; } catch ( UnknownHostExc e p t i o n e ) { System . err . println ( " Serverul nu poate fi gasit n " + e ) ; System . exit (1) ; } finally { if ( out != null ) out . close () ; if ( in != null ) in . close () ; if ( socket != null ) socket . close () ; } } } 13.5 Comunicarea prin datagrame In acest model nu exist˘ o conexiune permanent˘ ˆ a a ıntre client ¸i server prin s intermediul c˘reia s˘ se realizeze comunicarea. Clientul trimite cererea c˘tre a a a server prin intermediul unuia sau mai multor pachete de date independente, serverul le receptioneaz˘, extrage informatiile continute ¸i returneaz˘ r˘spunsul ¸ a ¸ ¸ s a a tot prin intermediul pachetelor. Un astfel de pachet se nume¸te datagram˘ s a ¸i este reprezentat printr-un obiect din clasa DatagramPacket. Rutarea s datagramelor de la o ma¸in˘ la alta se face exclusiv pe baza informatiilor s a ¸ continute de acestea. Primirea ¸i trimiterea datagramelor se realizeaz˘ prin ¸ s a intermediul unui socket, modelat prin intermediul clasei DatagramSocket.
  • 395.
    394 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ Dup˘ cum am mentionat deja, dezavantajul acestei metode este c˘ nu a ¸ a garanteaz˘ ajungerea la destinatie a pachetelor trimise ¸i nici c˘ vor fi primite a ¸ s a ˆ aceea¸i ordinie ˆ care au fost expediate. Pe de alt˘ parte, exist˘ situatii ın s ın a a ¸ ˆ care aceste lucruri nu sunt importante ¸i acest model este de preferat celui ın s bazat pe conexiuni care solicit˘ mult mai mult atˆt serverul cˆt ¸i clientul. De a a a s fapt, protocolul TCP/IP folose¸te tot pachete pentru trimiterea informatiilor s ¸ dintr-un nod ˆ altul al retelei, cu deosebirea c˘ asigur˘ respectarea ordinii de ın ¸ a a transmitere a mesajelor ¸i verific˘ ajungerea la destinatie a tuturor pachetelor s a ¸ - ˆ cazul ˆ care unul nu a ajuns, acesta va fi retrimis automat. ın ın Clasa DatagramPacket contine urm˘torii constructori: ¸ a DatagramPacket(byte[] buf, int length, InetAddress address, int port) DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) DatagramPacket(byte[] buf, int length, SocketAddress address) DatagramPacket(byte[] buf, int length) DatagramPacket(byte[] buf, int offset, int length) Primele dou˘ perechi de constructori sunt pentru creare pachetelor ce a vor fi expediate, diferenta ˆ ¸ ıntre ele fiind utilizarea claselor InetAddress, respectiv SocketAddress pentru specificarea adresei desinatie.¸ A trei pereche de constructori este folosit˘ pentru crearea unui pachet ˆ a ın care vor fi receptionate date, ei nespecificˆnd vreo surs˘ sau destinatie. ¸ a a ¸
  • 396.
    13.5. COMUNICAREA PRINDATAGRAME 395 Dup˘ crearea unui pachet procesul de trimitere ¸i primire a acestuia im- a s plic˘ apelul metodelor send ¸i receive ale clasei DatagramSocket. Deoarece a s toate informatii sunt incluse ˆ datagram˘, acela¸i socket poate fi folosit atˆt ¸ ın a s a pentru trimiterea de pachete, eventual c˘tre destinatii diferite, cˆt ¸i pentru a ¸ a s receptionarea acestora de la diverse surse. In cazul ˆ care refolosim pachete, ¸ ın putem schimba continutul acestora cu metoda setData, precum ¸i adresa la ¸ s care le trimitem prin setAddress, setPort ¸i setSocketAddress. s Extragerea informatiilor contiunte de un pachet se realizeaz˘ prin metoda ¸ ¸ a getData din clasa DatagramPacket. De asemenea, aceast˘ clas˘ ofer˘ metode a a a pentru aflarea adresei IP ¸i a portului procesului care a trimis datagrama, s pentru a-i putea r˘spunde dac˘ este necesar. Acestea sunt: getAdress, a a getPort ¸i getSocketAddress. s Listing 13.4: Structura unui server bazat pe datagrame import java . net .*; import java . io .*; public class DatagramServer { public static final int PORT = 8200; private DatagramSocket socket = null ; DatagramPacket cerere , raspuns = null ; public void start () throws IOException { socket = new DatagramSocket ( PORT ) ; try { while ( true ) { // Declaram pachetul in care va fi receptionata cererea byte [] buf = new byte [256]; cerere = new DatagramPacket ( buf , buf . length ) ; System . out . println ( " Asteptam un pachet ... " ) ; socket . receive ( cerere ) ; // Aflam adresa si portul de la care vine cererea InetAddress adresa = cerere . getAddress () ; int port = cerere . getPort () ; // Construim raspunsul
  • 397.
    396 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ String mesaj = " Hello " + new String ( cerere . getData () ); buf = mesaj . getBytes () ; // Trimitem un pachet cu raspunsul catre client raspuns = new DatagramPacket ( buf , buf . length , adresa , port ) ; socket . send ( raspuns ) ; } } finally { if ( socket != null ) socket . close () ; } } public static void main ( String [] args ) throws IOException { new DatagramServer () . start () ; } } Listing 13.5: Structura unui client bazat pe datagrame import java . net .*; import java . io .*; public class DatagramClient { public static void main ( String [] args ) throws IOException { // Adresa IP si portul la care ruleaza serverul InetAddress adresa = InetAddress . getByName ( " 127.0.0.1 " ) ; int port =8200; DatagramSocket socket = null ; DatagramPacket packet = null ; byte buf []; try { // Construim un socket pentru comunicare socket = new DatagramSocket () ; // Construim si trimitem pachetul cu cererea catre server buf = " Duke " . getBytes () ;
  • 398.
    ˘ 13.6. TRIMITEREA DEMESAJE CATRE MAI MULTI CLIENTI ¸ ¸ 397 packet = new DatagramPacket ( buf , buf . length , adresa , port ) ; socket . send ( packet ) ; // Asteaptam pachetul cu raspunsul de la server buf = new byte [256]; packet = new DatagramPacket ( buf , buf . length ) ; socket . receive ( packet ) ; // Afisam raspunsul (" Hello Duke !") System . out . println ( new String ( packet . getData () ) ) ; } finally { if ( socket != null ) socket . close () ; } } } 13.6 Trimiterea de mesaje c˘tre mai multi a ¸ clienti ¸ Diverse situatii impun gruparea mai multor clienti astfel ˆ at un mesaj (pa- ¸ ¸ ıncˆ chet) trimis pe adresa grupului s˘ fie receptionat de fiecare dintre ace¸tia. a ¸ s Gruparea mai multor programe ˆ vederea trimiterii multiple de mesaje se ın realizeaz˘ prin intermediul unui socket special, descris de clasa Multicast- a Socket, extensie a clasei DatagramSocket. Un grup de clienti abonati pentru trimitere multipl˘ este specificat printr- ¸ ¸ a o adres˘ IP din intervalul 224.0.0.1 - 239.255.255.255 ¸i un port UDP. a s Adresa 224.0.0.0 este rezervat˘ ¸i nu trebuie folosit˘. as a
  • 399.
    398 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸ Listing 13.6: Inregistrarea unui client ˆ ıntr-un grup import java . net .*; import java . io .*; public class MulticastClient { public static void main ( String [] args ) throws IOException { // Adresa IP si portul care reprezinta grupul de clienti InetAddress group = InetAddress . getByName ( " 230.0.0.1 " ) ; int port =4444; MulticastSocket socket = null ; byte buf []; try { // Ne alaturam grupului aflat la adresa si portul specificate socket = new MulticastSocket ( port ) ; socket . joinGroup ( group ) ; // Asteaptam un pachet venit pe adresa grupului buf = new byte [256]; DatagramPacket packet = new DatagramPacket ( buf , buf . length ) ; System . out . println ( " Asteptam un pachet ... " ) ; socket . receive ( packet ) ;
  • 400.
    ˘ 13.6. TRIMITEREA DEMESAJE CATRE MAI MULTI CLIENTI ¸ ¸ 399 System . out . println ( new String ( packet . getData () ) . trim () ) ; } finally { if ( socket != null ) { socket . leaveGroup ( group ) ; socket . close () ; } } } } Listing 13.7: Transmiterea unui mesaj c˘tre un grup a import java . net .*; import java . io .*; public class MulticastSend { public static void main ( String [] args ) throws IOException { InetAddress grup = InetAddress . getByName ( " 230.0.0.1 " ) ; int port = 4444; byte [] buf ; DatagramPacket packet = null ; // Cream un socket cu un numar oarecare DatagramSocket socket = new DatagramSocket (0) ; try { // Trimitem un pachet catre toti clientii din grup buf = ( new String ( " Salut grup ! " ) ) . getBytes () ; packet = new DatagramPacket ( buf , buf . length , grup , port ); socket . send ( packet ) ; } finally { socket . close () ; } } }
  • 401.
    400 CAPITOLUL 13. PROGRAMARE ˆ RETEA IN ¸
  • 402.
    Capitolul 14 Appleturi 14.1 Introducere Definitie¸ Un applet reprezint˘ un program Java de dimensiuni reduse ce gestioneaz˘ a a o suprafat˘ de afi¸are (container) care poate fi inclus˘ ˆ ¸a s a ıntr-o pagin˘ Web. Un a astfel de program se mai nume¸te miniaplicatie. s Ca orice alt˘ aplicatie Java, codul unui applet poate fi format din una sau a ¸ mai multe clase. Una dintre acestea este principal˘ ¸i extinde clasa Applet, as aceasta fiind clasa ce trebuie specificat˘ ˆ documentul HTML ce descrie a ın pagina Web ˆ care dorim s˘ includem appletul. ın a Diferenta fundamental˘ dintre un applet ¸i o aplicatie const˘ ˆ faptul c˘ ¸ a s ¸ a ın a un applet nu poate fi executat independent, ci va fi executat de browserul ˆ care este ˆ arcat˘ pagina Web ce contine appletul respectiv. O aplicatie ın ınc˘ a ¸ ¸ independent˘ este executat˘ prin apelul interpretorului java, avˆnd ca ar- a a a gument numele clasei principale a aplicatiei, clasa principal˘ fiind cea care ¸ a contine metoda main. Ciclul de viat˘ al unui applet este complet diferit, ¸ a fiind dictat de evenimentele generate de c˘tre browser la vizualizarea docu- a mentului HTML ce contine appletul. ¸ Pachetul care ofer˘ suport pentru crearea de appleturi este java.applet, a cea mai important˘ clas˘ fiind Applet. In pachetul javax.swing exist˘ a a a ¸i clasa JApplet, care extinde Applet, oferind suport pentru crearea de s appleturi pe arhitectura de componente JFC/Swing. 401
  • 403.
    402 CAPITOLUL 14. APPLETURI Ierarhia claselor din care deriv˘ appleturile este prezentata ˆ figura de a ın mai jos: Fiind derivat˘ din clasa Container, clasa Applet descrie de fapt suprafete a ¸ de afi¸are, asemenea claselor Frame sau Panel. s 14.2 Crearea unui applet simplu Crearea structurii de fi¸iere ¸i compilarea applet-urilor sunt identice ca ˆ s s ın cazul aplicatiilor. Difer˘ ˆ schimb structura programului ¸i modul de rulare ¸ a ın s a acestuia. S˘ parguream ˆ continuare ace¸ti pa¸i pentru a realiza un applet a ın s s extrem de simplu, care afi¸eaz˘ o imagine ¸i un ¸ir de caractere. s a s s 1. Scrierea codului sursa import java.awt.* ; import java.applet.* ; public class FirstApplet extends Applet { Image img; public void init() { img = getImage(getCodeBase(), "taz.gif"); }
  • 404.
    14.2. CREAREA UNUIAPPLET SIMPLU 403 public void paint (Graphics g) { g.drawImage(img, 0, 0, this); g.drawOval(100,0,150,50); g.drawString("Hello! My name is Taz!", 110, 25); } } Pentru a putea fi executat˘ de browser, clasa principal˘ a appletului tre- a a buie s˘ fie public˘. a a 2. Salvarea fisierelor surs˘ a Ca orice clas˘ public˘, clasa principala a appletului va fi salvat˘ ˆ a a a ıntr-un fi¸ier cu acela¸i nume ¸i extensia .java. A¸adar, vom salva clasa de mai sus s s s s ˆ ıntr-un fi¸ier FirstApplet.java. s 3. Compilarea Compilarea se face la fel ca ¸i la aplicatiile independente, folosind compi- s ¸ latorul javac apelat pentru fi¸ierul ce contine appletul. s ¸ javac FirstApplet.java In cazul ˆ care compilarea a reu¸it va fi generat fisierul FirstApplet.class. ın s 4. Rularea appletului Applet-urile nu ruleaza independent. Ele pot fi rulate doar prin inter- mediul unui browser: Internet Explorer, Netscape, Mozilla, Opera, etc. sau printr-un program special cum ar fi appletviewer din kitul de dezvoltare J2SDK. Pentru a executa un applet trebuie s˘ facem dou˘ operatii: a a ¸ • Crearea unui fi¸ier HTML ˆ care vom include applet-ul. S˘ con- s ın a sider˘m fi¸ierul simplu.html, avˆnd continutul de mai jos: a s a ¸
  • 405.
    404 CAPITOLUL 14. APPLETURI <html> <head> <title>Primul applet Java</title> </head> <body> <applet code=FirstApplet.class width=400 height=400> </applet> </body> </html> • Vizualizarea appletului: se deschide fisierul simplu.html folosind unul din browser-ele amintite sau efectuˆnd apelul: a appletviewer simplu.html. 14.3 Ciclul de viat˘ al unui applet ¸a Executia unui applet ˆ ¸ ıncepe ˆ momentul ˆ care un browser afi¸eaz˘ o pagin˘ ın ın s a a Web ˆ care este inclus appletul respectiv ¸i poate trece prin mai multe etape. ın s Fiecare etap˘ este strˆns legat˘ de un eveniment generat de c˘tre browser ¸i a a a a s determin˘ apelarea unei metode specifice din clasa ce implementeaz˘ apple- a a tul. • Inc˘rcarea ˆ memorie a ın Este creat˘ o instanta a clasei principale a appletului ¸i ˆ a ¸ s ıncarcat˘ ˆ a ın memorie. • Initializarea ¸ Este apelat˘ metoda init ce permite initializarea diverselor variabile, a ¸ citirea unor parametri de intrare, etc. • Pornirea Este apelat˘ metoda start a • Executia propriu-zis˘ ¸ a Const˘ ˆ interactiunea dintre utilizator ¸i componentele afi¸ate pe a ın ¸ s s suprafata appletului sau ˆ executarea unui anumit cod ˆ ¸ ın ıntr-un fir de executie. In unele situatii ˆ ¸ ¸ ıntreaga executie a appletului se consum˘ la ¸ a etapele de initializare ¸i pornire. ¸ s
  • 406.
    ¸˘ 14.3. CICLUL DEVIATA AL UNUI APPLET 405 • Oprirea temporar˘ a In cazul ˆ care utilizatorul p˘r˘se¸te pagina Web ˆ care ruleaz˘ apple- ın aa s ın a tul este apelat˘ metoda stop a acestuia, dˆndu-i astfel posibilitatea s˘ a a a opreasca temporar executia sa pe perioada ˆ care nu este vizibil, pentru ¸ ın a nu consuma inutil din timpul procesorului. Acela¸i lucru se ˆ ampl˘ s ıntˆ a dac˘ fereastra browserului este minimizat˘. In momentul cˆnd pagina a a a Web ce contine appletul devine din nou activ˘, va fi reapelat˘ metoda a a start. • Oprirea definitiv˘ a La ˆ ınchiderea tuturor instantelor browserului folosit pentru vizualizare, ¸ appletul va fi eliminat din memorie ¸i va fi apelat˘ metoda destroy a s a acestuia, pentru a-i permite s˘ elibereze resursele detinute. Apelul a ¸ metodei destroy este ˆ ıntotdeauna precedat de apelul lui stop. Metodele specifice appleturilor A¸adar, exist˘ o serie de metode specifice appleturilor ce sunt apelate au- s a tomat la diverse evenimente generate de c˘tre browser. Acestea sunt definite a ˆ clasa Applet ¸i sunt enumerate ˆ tabelul de mai jos: ın s ın Metoda Situatia ˆ care este apelat˘ ¸ ın a init La initializarea appletului. Teoretic, aceast˘ metod˘ ¸ a a ar trebui s˘ se apeleze o singur˘ dat˘, la prima a a a afi¸are a appletului ˆ pagin˘, ˆ a, la unele browsere, s ın a ıns˘ este posibil ca ea s˘ se apeleze de mai multe ori. a start Imediat dup˘ initializare ¸i de fiecare dat˘ a ¸ s a cˆnd appletul redevine activ, dup˘ o oprire temporar˘. a a a stop De fiecare dat˘ cˆnd appletul nu mai este vizibil a a (pagina Web nu mai este vizibil˘, fereastra browserului a este minimizat˘, etc) ¸i ˆ a s ınainte de destroy. destroy La ˆ ınchiderea ultimei instante a browserului ¸ care a ˆ arcat ˆ memorie clasa principal˘ a appletului. ınc˘ ın a Atentie ¸
  • 407.
    406 CAPITOLUL 14. APPLETURI Aceste metode sunt apelate automat de browser ¸i nu trebuie apelate s explicit din program ! Structura general˘ a unui applet a import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class StructuraApplet extends Applet { public void init() { } public void start() { } public void stop() { } public void destroy() { } } 14.4 Interfata grafic˘ cu utilizatorul ¸ a Dup˘ cum am v˘zut, clasa Applet este o extensie a superclasei Container, a a ceea ce ˆ ınseamn˘ c˘ appleturile sunt, ˆ a a ınainte de toate, suprafete de afi¸are. ¸ s Plasarea componentelor, gestionarea pozition˘rii lor ¸i tratarea evenimentelor ¸ a s generate se realizeaz˘ la fel ca ¸i ˆ cazul aplicatiilor. Uzual, ad˘ugarea com- a s ın ¸ a ponentelor pe suprafata appletului precum ¸i stabilirea obiectelor respons- ¸ s abile cu tratarea evenimentelor generate sunt operatiuni ce vor fi realizate ˆ ¸ ın metoda init.
  • 408.
    ¸ ˘ 14.4. INTERFATA GRAFICA CU UTILIZATORUL 407 Gestionarul de pozitionare implicit este FlowLayout, ˆ a acesta poate fi ¸ ıns˘ schimbat prin metoda setLayout.
  • 409.
    408 CAPITOLUL 14. APPLETURI Desenarea pe suprafata unui applet ¸ Exist˘ o categorie ˆ a ıntreag˘ de appleturi ce nu comunic˘ cu utilizatorul a a prin intermediul componentelor ci, executia lor se rezum˘ la diverse operatiuni ¸ a ¸ de desenare realizate ˆ metoda paint. Reamintim c˘ metoda paint este re- ın a sponsabil˘ cu definirea aspectului grafic al oric˘rei componente. Implicit, a a metoda paint din clasa Applet nu realizeaz˘ nimic, deci, ˆ cazul ˆ care a ın ın dorim s˘ desen˘m direct pe suprafata unui applet va fi nevoie s˘ supradefinim a a ¸ a aceast˘ metod˘. a a public void paint(Graphics g) { // Desenare ... } In cazul ˆ care este aleas˘ aceast˘ solutie, evenimentele tratate uzual vor fi ın a a ¸ cele generate de mouse sau tastatur˘. a 14.5 Definirea ¸i folosirea parametrilor s Parametrii sunt pentru appleturi ceea ce argumentele de la linia de comand˘ a sunt pentru aplicatiile independente. Ei permit utilizatorului s˘ personalizeze ¸ a aspectul sau comportarea unui applet f˘r˘ a-i schimba codul ¸i recompila aa s clasele. Definirea parametrilor se face ˆ cadrul tagului APPLET din documen- ın tul HTML ce contine appletul ¸i sunt identificati prin atributul PARAM. ¸ s ¸ Fiecare parametru are un nume, specificat prin NAME ¸i o valoare, speci- s ficat˘ prin VALUE, ca ˆ exemplul de mai jos: a ın <APPLET CODE="TestParametri.class" WIDTH=100 HEIGHT=50 <PARAM NAME=textAfisat VALUE="Salut"> <PARAM NAME=numeFont VALUE="Times New Roman"> <PARAM NAME=dimFont VALUE=20> </APPLET> Ca ¸i ˆ cazul argumentelor trimise aplicatiilor de la linia de comand˘, tipul s ın ¸ a parametrilor este ˆ ıntotdeauna ¸ir de caractere, indiferent dac˘ valoarea s a este ˆ ıntre ghilimele sau nu. Fiecare applet are ¸i un set de parametri prestabiliti ale c˘ror nume nu s ¸ a vor putea fi folosite pentru definirea de noi parametri folosind metoda de
  • 410.
    14.5. DEFINIREA SIFOLOSIREA PARAMETRILOR ¸ 409 mai sus. Ace¸tia apar direct ˆ corpul tagului APPLET ¸i definesc informatii s ın s ¸ generale despre applet. Exemple de astfel de parametri sunt CODE, WIDTH sau HEIGHT. Lista lor complet˘ va fi prezentata la descrierea tagului APPLET. a Folosirea parametrilor primiti de c˘tre un applet se face prin intermediul ¸ a metodei getParameter care prime¸te ca argument numele unui parametru s ¸i returneaz˘ valoarea acestuia. In cazul ˆ care nu exist˘ nici un parametru s a ın a cu numele specificat, metoda ˆ ıntoarce null, caz ˆ care programul trebuie ın s˘ atribuie o valoare implicit˘ variabilei ˆ care se dorea citirea respectivului a a ın parametru. Orice applet poate pune la dispozitie o ”documentatie” referitoare la para- ¸ ¸ metrii pe care ˆ suport˘, pentru a veni ˆ ajutorul utilizatorilor care doresc s˘ ıi a ın a includ˘ appletul ˆ a ıntr-o pagin˘ Web. Aceasta se realizeaz˘ prin supradefinirea a a metodei getParameterInfo, care returneaz˘ un vector format din triplete a de ¸iruri. Fiecare element al vectorului este de fapt un vector cu trei elemente s de tip String, cele trei ¸iruri reprezentˆnd numele parametrului, tipul s˘u ¸i s a a s o descriere a sa. Informatiile furnizate de un applet pot fi citite din browserul ¸ folosit pentru vizualizare prin metode specifice acestuia. De exemplu, ˆ ap-ın pletviewer informatiile despre parametri pot fi vizualizate la rubrica Info din ¸ meniul Applet, ˆ Netscape se folose¸te optiunea Page info din meniul View, ın s ¸ etc. S˘ scriem un applet care s˘ afi¸eze un text primit ca parametru, folosind a a s un font cu numele ¸i dimensiunea specificate de asemenea ca parametri. s Listing 14.1: Folosirea parametrilor import java . applet . Applet ; import java . awt .*; public class TestParametri extends Applet { String text , numeFont ; int dimFont ; public void init () { text = getParameter ( " textAfisat " ) ; if ( text == null ) text = " Hello " ; // valoare implicita numeFont = getParameter ( " numeFont " ) ; if ( numeFont == null ) numeFont = " Arial " ;
  • 411.
    410 CAPITOLUL 14. APPLETURI try { dimFont = Integer . parseInt ( getParameter ( " dimFont " ) ) ; } catch ( Numb erFo r m at Ex ce pt i o n e ) { dimFont = 16; } } public void paint ( Graphics g ) { g . setFont ( new Font ( numeFont , Font . BOLD , dimFont ) ) ; g . drawString ( text , 20 , 20) ; } public String [][] getParameterInfo () { String [][] info = { // Nume Tip Descriere { " textAfisat " , " String " , " Sirul ce va fi afisat " } , { " numeFont " , " String " , " Numele fontului " } , { " dimFont " , " int " , " Dimensiunea fontului " } }; return info ; } } 14.6 Tag-ul APPLET Sintaxa complet˘ a tagului APPLET, cu ajutorul c˘ruia pot fi incluse apple- a a turi ˆ cadrul paginilor Web este: ın <APPLET CODE = clasaApplet WIDTH = latimeInPixeli HEIGHT = inaltimeInPixeli [ARCHIVE = arhiva.jar] [CODEBASE = URLApplet] [ALT = textAlternativ] [NAME = numeInstantaApplet] [ALIGN = aliniere] [VSPACE = spatiuVertical]
  • 412.
    14.6. TAG-UL APPLET 411 [HSPACE = spatiuOrizontal] > [< PARAM NAME = parametru1 VALUE = valoare1 >] [< PARAM NAME = parametru2 VALUE = valoare2 >] ... [text HTML alternativ] </APPLET> Atributele puse ˆ ıntre paranteze p˘trate sunt optionale. a ¸ • CODE = clasaApplet Numele fi¸ierului ce contine clasa principal˘ a appletului. Acesta va s ¸ a fi c˘utat ˆ directorul specificat de CODEBASE. Nu poate fi absolut ¸i a ın s trebuie obligatoriu specificat. Extensia ”.class” poate sau nu s˘ apar˘. a a • WIDTH =latimeInPixeli, HEIGHT =inaltimeInPixeli Specific˘ l˘¸imea ¸i ˆ altimea suprafetei ˆ care va fi afi¸at appletul. a at s ın˘ ¸ ¸ ın s Sunt obligatorii. • ARCHIVE = arhiva.jar Specific˘ arhiva ˆ care se g˘sesc clasele appletului. a ın a • CODEBASE = directorApplet Specific˘ URL-ul la care se g˘se¸te clasa appletului. Uzual se exprim˘ a a s a relativ la directorul documentului HTML. In cazul ˆ care lipse¸te, se ın s consider˘ implicit URL-ul documentului. a • ALT = textAlternativ Specific˘ textul ce trebuie afi¸at dac˘ browserul ˆ ¸elege tagul APPLET a s a ınt dar nu poate rula appleturi Java. • NAME =numeInstantaApplet Ofer˘ posibilitatea de a da un nume respectivei instante a appletu- a ¸ lui, astfel ˆ at mai multe appleturi aflate pe aceea¸i pagin˘ s˘ poat˘ ıncˆ s a a a comunica ˆ ıntre ele folosindu-se de numele lor. • ALIGN =aliniere Semnific˘ modalitatea de aliniere a appletului ˆ pagina Web. Acest a ın atribut poate primi una din urm˘toarele valori: left, right, top, a
  • 413.
    412 CAPITOLUL 14. APPLETURI texttop, middle, absmiddle, baseline, bottom, absbottom , seminificatiile ¸ lor fiind acelea¸i ca ¸i la tagul IMG. s s • VSPACE =spatiuVertical, HSPACE = spatiuOrizontal Specific˘ numarul de pixeli dintre applet ¸i marginile suprafetei de a s afi¸are. s • PARAM Tag-urile PARAM sunt folosite pentru specificarea parametrilor unui ap- plet (vezi ”Folosirea parametrilor”). • text HTML alternativ Este textul ce va fi afi¸at ˆ cazul ˆ care browserul nu ˆ s ın ın ıntelege tagul APPLET. Browserele Java-enabled vor ignora acest text. 14.7 Folosirea firelor de executie ˆ appleturi ¸ ın La ˆ arcarea unei pagini Web, fiec˘rui applet ˆ este creat automat un fir ınc˘ a ıi de executie responsabil cu apelarea metodelor acestuia. Acestea vor rula ¸ concurent dup˘ regulile de planificare implementate de ma¸ina virtual˘ Java a s a a platformei folosite. Din punctul de vedere al interfetei grafice ˆ a, fiecare applet aflat pe o ¸ ıns˘ pagin˘ Web are acces la un acela¸i fir de executie, creat de asemenea automat a s ¸ de c˘tre browser, ¸i care este responsabil cu desenarea appletului (apelul a s metodelor update ¸i paint) precum ¸i cu transmiterea mesajelor generate s s de c˘tre componente. Intrucˆt toate appleturile de pe pagin˘ ”ˆ a a a ımpart” acest fir de executie, nici unul nu trebuie s˘ ˆ solicite ˆ mod excesiv, deoarece va ¸ a ıl ın provoca functionarea anormal˘ sau chiar blocarea celorlalte. ¸ a In cazul ˆ care dorim s˘ efectu˘m operatiuni consumatoare de timp este ın a a ¸ recomandat s˘ le realiz˘m ˆ a a ıntr-un alt fir de executie, pentru a nu bloca ¸ interactiunea utilizatorului cu appletul, redesenarea acestuia sau activitatea ¸ celorlalte appleturi de pe pagin˘. a S˘ considerm˘m mai ˆ ai dou˘ abord˘ri gre¸ite de lucru cu appleturi. a a ıntˆ a a s Dorim s˘ cre˘m un applet care s˘ afi¸eze la coordonate aleatoare mesajul a a a s ”Hello”, cu pauz˘ de o secund˘ ˆ a a ıntre dou˘ afi¸˘ri. Prima variant˘, gre¸it˘ de a sa a s a altfel, ar fi:
  • 414.
    14.7. FOLOSIREA FIRELORDE EXECUTIE ˆ APPLETURI ¸ IN 413 Listing 14.2: Incorect: blocarea metodei paint import java . applet .*; import java . awt .*; public class AppletRau1 extends Applet { public void paint ( Graphics g ) { while ( true ) { int x = ( int ) ( Math . random () * getWidth () ) ; int y = ( int ) ( Math . random () * getHeight () ) ; g . drawString ( " Hello " , x , y ) ; try { Thread . sleep (1000) ; } catch ( InterruptedE xc ep ti on e ) {} } } } Motivul pentru care acest applet nu functioneaz˘ corect ¸i probabil va ¸ a s duce la anomalii ˆ functionarea browserului este c˘ firul de executie care ın ¸ a ¸ se ocup˘ cu desenarea va r˘mˆne blocat ˆ metoda paint, ˆ a a a ın ıncercˆnd s˘ o a a termine. Ca regul˘ general˘, codul metodei paint trebuie s˘ fie cˆt mai a a a a simplu de executat ceea ce, evident, nu este cazul ˆ appletul de mai sus. ın O alt˘ idee de rezolvare care ne-ar putea veni, de asemenea gre¸it˘, este a s a urm˘toarea : a Listing 14.3: Incorect: appletul nu termin˘ initializarea a ¸ import java . applet .*; import java . awt .*; public class AppletRau2 extends Applet { int x , y ; public void init () { while ( true ) { x = ( int ) ( Math . random () * getWidth () ) ; y = ( int ) ( Math . random () * getHeight () ) ; repaint () ; try { Thread . sleep (1000) ; } catch ( InterruptedE x ce pt io n e ) {} } } public void paint ( Graphics g ) {
  • 415.
    414 CAPITOLUL 14. APPLETURI g . drawString ( " Hello " , x , y ) ; } } Pentru a putea da o solutie corect˘ problemei propuse, trebuie s˘ folosim ¸ a a un fir de executie propriu. Structura unui applet care doreste s˘ lanseze un ¸ a fir de executie poate avea dou˘ forme. In prima situatie appletul porne¸te ¸ a ¸ s firul la initialzarea sa iar acesta va rula, indiferent dac˘ appletul mai este sau ¸ a nu vizibil, pˆn˘ la oprirea sa natural˘ (terminarea metodei run) sau pˆn˘ la a a a a a ˆ ınchiderea sesiunii de lucru a browserului. Listing 14.4: Corect: folosirea unui fir de executie propriu ¸ import java . applet .*; import java . awt .*; public class AppletCorect1 extends Applet implements Runnable { int x , y ; Thread fir = null ; public void init () { if ( fir == null ) { fir = new Thread ( this ) ; fir . start () ; } } public void run () { while ( true ) { x = ( int ) ( Math . random () * getWidth () ) ; y = ( int ) ( Math . random () * getHeight () ) ; repaint () ; try { Thread . sleep (1000) ; } catch ( Inter ru pt ed Ex ce pt io n e ) {} } } public void paint ( Graphics g ) { g . drawString ( " Hello " , x , y ) ; } } In cazul ˆ care firul de executie pornit de applet efectueaz˘ operatii ce ın ¸ a
  • 416.
    14.7. FOLOSIREA FIRELORDE EXECUTIE ˆ APPLETURI ¸ IN 415 au sens doar dac˘ appletul este vizibil, cum ar fi animatie, ar fi de dorit a ca acesta s˘ se opreasca atunci cˆnd appletul nu mai este vizibil (la apelul a a metodei stop) ¸i s˘ reporneasca atunci cˆnd appletul redevine vizibil (la s a a apelul metodei start). Un applet este considerat activ imediat dup˘ apelul a metodei start ¸i devine inactiv la apelul metodei stop. Pentru a afla dac˘ s a un applet este activ se folose¸te metoda isActive. s S˘ modific˘m programul anterior, ad˘ugˆnd ¸i un contor care s˘ numere a a a a s a afi¸˘rile de mesaje - acesta nu va fi incrementat pe perioada ˆ care appletul sa ın nu este activ. Listing 14.5: Folosirea metodelor start ¸i stop s import java . applet .*; import java . awt .*; public class AppletCorect2 extends Applet implements Runnable { int x , y ; Thread fir = null ; boolean activ = false ; int n = 0; public void start () { if ( fir == null ) { fir = new Thread ( this ) ; activ = true ; fir . start () ; } } public void stop () { activ = false ; fir = null ; } public void run () { while ( activ ) { x = ( int ) ( Math . random () * getWidth () ) ; y = ( int ) ( Math . random () * getHeight () ) ; n ++; repaint () ; try { Thread . sleep (1000) ; } catch ( InterruptedE x c e p t i o n e ) {}
  • 417.
    416 CAPITOLUL 14. APPLETURI } } public void paint ( Graphics g ) { g . drawString ( " Hello " + n , x , y ) ; } } Atentie ¸ Este posibil ca unele browsere s˘ nu apele metoda stop ˆ situatiile a ın ¸ prev˘zute ˆ specficiatiile appleturilor. Din acest motiv, corectitudinea unui a ın ¸ applet nu trebuie s˘ se bazeze pa acest mecanism. a 14.8 Alte metode oferite de clasa Applet Pe lˆng˘ metodele de baz˘: init, start, stop, destroy, clasa Applet a a a ofer˘ metode specifice applet-urilor cum ar fi: a Punerea la dispozitie a unor informatii despre applet ¸ Similar˘ cu metoda getParameterInfo ce oferea o ”documentatie” despre a ¸ parametrii pe care ˆ accept˘ un applet, exist˘ metoda getAppletInfo ce per- ıi a a mite specificarea unor informatii legate de applet cum ar fi numele, autorul, ¸ versiunea, etc. Metoda returneaz˘ un sir de caractere continˆnd informatiile a a ¸ respective. public String getAppletInfo() { return "Applet simplist, autor necunoscut, ver 1.0"; } Aflarea adreselor URL referitoare la applet Se realizeaz˘ cu metodele: a
  • 418.
    14.8. ALTE METODEOFERITE DE CLASA APPLET 417 • getCodeBase - ce returneaz˘ URL-ul directorului ce contine clasa a ¸ appletului; • getDocumentBase - returneaz˘ URL-ul directorului ce contine doc- a ¸ umentul HTML ˆ care este inclus appletul respectiv. ın Aceste metode sunt foarte utile deoarece permit specificarea relativ˘ a unor a fi¸iere folosite de un applet, cum ar fi imagini sau sunete. s Afi¸area unor mesaje ˆ bara de stare a browserului s ın Acest lucru se realizeaz˘ cu metoda showStatus a public void init() { showStatus("Initializare applet..."); } Afi¸area imaginilor s Afi¸area imaginilor ˆ s ıntr-un applet se face fie prin intermediul unei compo- nente ce permite acest lucru, cum ar fi o suprafat˘ de desenare de tip Canvas, ¸a fie direct ˆ metoda paint a applet-ului, folosind metoda drawImage a clasei ın Graphics. In ambele cazuri, obtinerea unei referinte la imaginea respectiv˘ ¸ ¸ a se va face cu ajutorul metodei getImage din clasa Applet. Aceasta poate primi ca argument fie adresa URL absolut˘ a fi¸ierului ce reprezint˘ imag- a s a inea, fie calea relativ˘ la o anumit˘ adres˘ URL, cum ar fi cea a directorului a a a ˆ care se g˘se¸te documentul HTML ce contine appletul (getDocumentBase) ın a s ¸ sau a directorului ˆ care se g˘se¸te clasa appletului (getCodeBase). ın a s Listing 14.6: Afi¸area imaginilor s import java . applet . Applet ; import java . awt .*; public class Imagini extends Applet { Image img = null ; public void init () { img = getImage ( getCodeBase () , " taz . gif " ) ; }
  • 419.
    418 CAPITOLUL 14. APPLETURI public void paint ( Graphics g ) { g . drawImage ( img , 0 , 0 , this ) ; } } Aflarea contextului de executie ¸ Contextul de executie al unui applet se refer˘ la pagina ˆ care acesta ruleaz˘, ¸ a ın a eventual ˆ ımpreun˘ cu alte appleturi, ¸i este descris de interfata AppletCon- a s ¸ text. Crearea unui obiect ce implementeaz˘ aceast˘ interfat˘ se realizeaz˘ de a a ¸a a c˘tre browser, la apelul metodei getAppletContext a clasei Applet. Prin a intermediul acestei interfete un applet poate ”vedea” ˆ jurul sau, putˆnd ¸ ın a comunica cu alte applet-uri aflate pe aceeasi pagin˘ sau cere browser-ului s˘ a a deschid˘ diverse documente. a AppletContext contex = getAppletContext(); Afi¸area unor documente ˆ browser s ın Se face cu metoda showDocument ce prime¸te adresa URL a fi¸ierului ce s s contine documentul pe care dorim sa-l deschidem (text, html, imagine, etc). ¸ Aceast˘ metod˘ este accesat˘ prin intermediul contextului de executie al a a a ¸ appletului. try { URL doc = new URL("http://www.infoiasi.ro"); getAppletContext().showDocument(doc); } catch(MalformedURLException e) { System.err.println("URL invalid! n" + e); } Comunicarea cu alte applet-uri Aceast˘ comunicare implic˘ de fapt identificarea unui applet aflat pe aceea¸i a a s pagina ¸i apelarea unei metode sau setarea unei variabile publice a acestuia. s Identificarea se face prin intermediu numelui pe care orice instanta a unui ¸ applet ˆ poate specifica prin atributul NAME. ıl
  • 420.
    14.8. ALTE METODEOFERITE DE CLASA APPLET 419 Obtinerea unei referinte la un applet al c˘rui nume ˆ cunoa¸tem sau ¸ ¸ a ıl s obtinerea unei enumer˘ri a tuturor applet-urilor din pagin˘ se fac prin in- ¸ a a termediul contextului de executie, folosind metodele getApplet, respectiv ¸ getApplets. Redarea sunetelor Clasa Applet ofer˘ ¸i posibilitatea red˘rii de sunete ˆ format .au. Acestea as a ın sunt descrise prin intermediul unor obiecte ce implementeaz˘ interfata Au- a ¸ dioClip din pachetul java.applet. Pentru a reda un sunet aflat ˆ ıntr-un fi¸ier ”.au” la un anumit URL exist˘ dou˘ posibilit˘¸i: s a a at • Folosirea metodei play din clasa Applet care prime¸te ca argument s URL-ul la care se afl˘ sunetul; acesta poate fi specificat absolut sau a relativ la URL-ul appletului • Crearea unui obiect de tip AudioClip cu metoda getAudioClip apoi apelarea metodelor start, loop ¸i stop pentru acesta. s Listing 14.7: Redarea sunetelor import java . applet .*; import java . awt .*; import java . awt . event .*; public class Sunete extends Applet implements ActionListener { Button play = new Button ( " Play " ) ; Button loop = new Button ( " Loop " ) ; Button stop = new Button ( " Stop " ) ; AudioClip clip = null ; public void init () { // Fisierul cu sunetul trebuie sa fie in acelasi // director cu appletul clip = getAudioClip ( getCodeBase () , " sunet . au " ) ; add ( play ) ; add ( loop ) ; add ( stop ) ; play . addActionListener ( this ) ; loop . addActionListener ( this ) ; stop . addActionListener ( this ) ;
  • 421.
    420 CAPITOLUL 14. APPLETURI } public void actionPerformed ( ActionEvent e ) { Object src = e . getSource () ; if ( src == play ) clip . play () ; else if ( src == loop ) clip . loop () ; else if ( src == stop ) clip . stop () ; } } In cazul ˆ care appletul folose¸te mai multe tipuri de sunete, este reco- ın s mandat ca ˆ arcarea acestora s˘ fie f˘cut˘ ˆ ınc˘ a a a ıntr-un fir de executie separat, ¸ pentru a nu bloca temporar activitatea fireasc˘ a programului. a 14.9 Arhivarea appleturilor Dup˘ cum am v˘zut, pentru ca un applet aflat pe o pagin˘ Web s˘ poat˘ a a a a a fi executat codul s˘u va fi transferat de pe serverul care g˘zduie¸te pagina a a s Web solicitat˘ pe ma¸ina clientului. Deoarece transferul datelor prin retea a s ¸ este un proces lent, cu cˆt dimensiunea fi¸ierlor care formeaz˘ appletul este a s a mai redus˘, cu atˆ ˆ arcarea acestuia se va face mai repede. Mai mult, dac˘ a a ınc˘ a appletul contine ¸i alte clase ˆ afar˘ de cea principal˘ sau diverse resurse ¸ s ın a a (imagini, sunete, etc), acestea vor fi transferate prin retea abia ˆ momentul ¸ ın ˆ care va fi nevoie de ele, oprind temporar activitatea appletului pˆn˘ la ın a a ˆ arcarea lor. Din aceste motive, cea mai eficient˘ modalitate de a distribui ınc˘ a un applet este s˘ arhiv˘m toate fi¸ierele necesare acestuia. a a s Arhivarea fi¸ierelor unui applet se face cu utilitarul jar, oferit ˆ s ın distributia J2SDK. ¸ // Exemplu jar cvf arhiva.jar ClasaPrincipala.class AltaClasa.class imagine.jpg sunet.au // sau jar cvf arhiva.jar *.class *.jpg *.au
  • 422.
    14.10. RESTRICTII DESECURITATE ¸ 421 Includerea unui applet arhivat ˆ ıntr-o pagin˘ Web se realizeaz˘ spe- a a cific˘nd pe lˆng˘ numele clasei principale ¸i numele arhivei care o contine: a a a s ¸ <applet archive=arhiva.jar code=ClasaPrincipala width=400 height=200 /> 14.10 Restrictii de securitate ¸ Deoarece un applet se execut˘ pe ma¸ina utilizatorului care a solicitat pag- a s ina Web ce contine appletul respectiv, este foarte important s˘ existe anu- ¸ a mite restrictii de securitate care s˘ controleze activitatea acestuia, pentru a ¸ a preveni actiuni r˘u intentionate, cum ar fi ¸tergeri de fi¸iere, etc., care s˘ ¸ a ¸ s s a aduc˘ prejudicii utilizatorului. Pentru a realiza acest lucru, procesul care a ruleaz˘ appleturi instaleaz˘ un manager de securitate, adic˘ un obiect de a a a tip SecurityManager care va ”superviza” activitatea metodelor appletului, aruncˆnd exceptii de tip Security Exception ˆ cazul ˆ care una din aces- a ¸ ın ın tea ˆ ıncearc˘ s˘ efectueze o operatie nepermis˘. a a ¸ a Un applet nu poate s˘:a • Citeasc˘ sau s˘ scrie fi¸iere pe calculatorul pe care a fost ˆ a a s ıncarcat (client). • Deschid˘ conexiuni cu alte ma¸ini ˆ afar˘ de cea de pe care provine a s ın a (host). • Porneasc˘ programe pe ma¸ina client. a s • Citeasc˘ diverse propriet˘¸i ale sistemului de operare al clientului. a at Ferestrele folosite de un applet, altele decˆt cea a browserului, vor ar˘ta a a altfel decˆt ˆ a ıntr-o aplicatie obi¸nuit˘, indicˆnd faptul c˘ au fost create de un ¸ s a a a applet. 14.11 Appleturi care sunt ¸i aplicatii s ¸ Deoarece clasa Applet este derivat˘ din Container, deci ¸i din Component, a s ea descrie o suprafat˘ de afi¸are care poate fi inclus˘ ca orice alt˘ component˘ ¸a s a a a ˆ ıntr-un alt container, cum ar fi o fereastr˘. Un applet poate functiona ¸i ca a ¸ s o aplicatie independent˘ astfel: ¸ a
  • 423.
    422 CAPITOLUL 14. APPLETURI • Ad˘ug˘m metoda main clasei care descrie appletul, ˆ care vom face a a ın operatiunile urm˘toare. ¸ a • Cre˘m o instant˘ a appletului ¸i o ad˘ug˘m pe suprafata unei ferestre. a ¸a s a a ¸ • Apel˘m metodele init ¸i start, care ar fi fost apelate automat de a s c˘tre browser. a • Facem fereastra vizibil˘. a Listing 14.8: Applet ¸i aplicatie s ¸ import java . applet . Applet ; import java . awt .*; public class AppletAplicatie extends Applet { public void init () { add ( new Label ( " Applet si aplicatie " ) ) ; } public static void main ( String args []) { AppletAplicatie applet = new AppletAplicatie () ; Frame f = new Frame ( " Applet si aplicatie " ) ; f . setSize (200 , 200) ; f . add ( applet , BorderLayout . CENTER ) ; applet . init () ; applet . start () ; f . show () ; } }
  • 424.
    Capitolul 15 Lucrul cubaze de date 15.1 Introducere 15.1.1 Generalit˘¸i despre baze de date at Aplicatiile care folosesc baze de date sunt, ˆ general, aplicatii complexe ¸ ın ¸ folosite pentru gestionarea unor informatii de dimensiuni mari ˆ ¸ ıntr-o manier˘ a sigur˘ ¸i eficient˘. as a Ce este o baz˘ de date ? a La nivelul cel mai general, o baz˘ de date reprezint˘ o modalitate de stocare a a a unor informatii (date) pe un suport extern, cu posibilitatea reg˘sirii acestora. ¸ a Uzual, o baz˘ de date este memorat˘ ˆ a a ıntr-unul sau mai multe fi¸iere. s Modelul clasic de baze de date este cel relational, ˆ care datele sunt ¸ ın memorate ˆ tabele. Un tabel reprezint˘ o structur˘ de date format˘ dintr-o ın a a a multime de articole, fiecare articol avˆnd definite o serie de atribute - aceste ¸ a atribute corespund coloanelor tabelului, ˆ timp ce o linie va reprezenta un ın articol. Pe lˆnga tabele, o baz˘ de date mai poate contine: proceduri ¸i a a ¸ s functii, utilizatori ¸i grupuri de utilizatori, tipuri de date, obiecte, etc. ¸ s Dintre produc˘torii cei mai importanti de baze de date amintim com- a ¸ paniile Oracle, Sybase, IBM, Informix, Microsoft, etc. fiecare furnizˆnd o a serie ˆıntreag˘ de produse ¸i utilitare pentru lucrul cu baze de date. Aceste a s produse sunt ˆ general referite prin termenii DBMS (Database Management ın System) sau, ˆ traducere, SGBD (Sistem de Gestiune a Bazelor de Date). In ın acest capitol vom analiza lucrul cu baze de date din perspectiva program˘rii a 423
  • 425.
    424 CAPITOLUL 15. LUCRUL CU BAZE DE DATE ˆ limbajul Java, f˘r˘ a descrie particularit˘¸i ale unei solutii de stocare a ın aa at ¸ datelor anume. Vom vedea c˘, folosind Java, putem crea aplicatii care s˘ a ¸ a ruleze f˘r˘ nici o modificare folosind diverse tipuri de baze care au aceea¸i aa s structur˘, ducˆnd ˆ felul acesta notiunea de portabilitate ¸i mai departe. a a ın ¸ s Crearea unei baze de date Crearea unei baze de date se face uzual folosind aplicatii specializate oferite ¸ de produc˘torul tipului respectiv de sistem de gestiune a datelor, dar exist˘ a a ¸i posibilitatea de a crea o baza folosind un script SQL. Acest aspect ne va s preocupa ˆ a mai putin, exemplele prezentate presupunˆnd c˘ baza a fost ıns˘ ¸ a a creat˘ deja ¸i are o anumit˘ structur˘ specificat˘. a s a a a Accesul la baza de date Se face prin intermediul unui driver specific tipului respectiv de SGBD. Acesta este responsabil cu accesul efectiv la datele stocate, fiind legatura dintre aplicatie ¸i baza de date. ¸ s Limbajul SQL SQL (Structured Query Language) reprezint˘ un limaj de programare ce a permite interogarea ¸i actualizarea informatiilor din baze de date relationale. s ¸ ¸ Acesta este standardizat astfel ˆ at diverse tipuri de drivere s˘ se comporte ıncˆ a identic, oferind astfel o modalitate unitar˘ de lucru cu baze de date. a 15.1.2 JDBC JDBC (Java Database Connectivity) este o interfat˘ standard SQL de acces ¸a la baze de date. JDBC este constituit˘ dintr-un set de clase ¸i interfete a s ¸
  • 426.
    ˘ 15.2. CONECTAREA LAO BAZA DE DATE 425 scrise ˆ Java, furnizˆnd mecanisme standard pentru proiectantii aplicatiilor ın a ¸ ¸ ce folosesc de baze de date. Folosind JDBC este u¸or s˘ transmitem secvente SQL c˘tre baze de date s a ¸ a relationale. Cu alte cuvinte, nu este necesar s˘ scriem un program pentru a ¸ a accesa o baz˘ de date Oracle, alt program pentru a accesa o baz˘ de date a a Sybase ¸i asa mai departe. Este de ajuns s˘ scriem un singur program folosind s a API-ul JDBC ¸i acesta va fi capabil s˘ comunice cu drivere diferite, trimitˆnd s a ¸a secvente SQL c˘tre baza de date dorit˘. Bineˆ ¸eles, scriind codul surs˘ ˆ ¸ a a ınt a ın Java, ne este asigurat˘ portabilitatea programului. Deci, iat˘ dou˘ motive a a a puternice care fac combinatia Java - JDBC demn˘ de luat ˆ seam˘. ¸ a ın a Pachetele care ofer˘ suport pentru lucrul cu baze de date sunt java.sql a ce reprezint˘ nucleul tehnologiei JDBC ¸i, preluat de pe platforma J2EE, a s javax.sql. In linii mari, API-ul JDBC ofer˘ urm˘toarele facilit˘¸i: a a at 1. Stabilirea unei conexiuni cu o baz˘ de date. a 2. Efectuarea de secvente SQL. ¸ 3. Prelucrarea rezultatelor obtinute. ¸ 15.2 Conectarea la o baz˘ de date a Procesul de conectare la o baz˘ de date implic˘ efectuarea a dou˘ operatii: a a a ¸ 1. Inregistrarea unui driver corespunz˘tor. a 2. Realizarea unei conexiuni propriu-zise. Definitie ¸ O conexiune (sesiune) la o baz˘ de date reprezint˘ un context prin care a a sunt trimise secvente SQL ¸i primite rezultate. Intr-o aplicatie pot exista ¸ s ¸ simultan mai multe conexiuni la baze de date diferite sau la aceea¸i baz˘. s a Clasele ¸i interfetele responsabile cu realizarea unei conexiuni sunt: s ¸ • DriverManager - este clasa ce se ocup˘ cu ˆ a ınregistrarea driverelor ce vor fi folosite ˆ aplicatie; ın ¸
  • 427.
    426 CAPITOLUL 15. LUCRUL CU BAZE DE DATE • Driver - interfata pe care trebuie s˘ o implementeze orice clas˘ ce ¸ a a descrie un driver; • DriverPropertyInfo - prin intermediul acestei clase pot fi specificate diverse propriet˘¸i ce vor fi folosite la realizarea conexiunilor; at • Connection - descrie obiectele ce modeleaz˘ o conexiune propriu-zis˘ a a cu baza de date. 15.2.1 Inregistrarea unui driver Primul lucru pe care trebuie s˘-l fac˘ o aplicatie ˆ procesul de conectare a a ¸ ın la o baz˘ de date este s˘ ˆ a a ınregistreze la ma¸ina virtual˘ ce ruleaz˘ aplicatia s a a ¸ driverul JDBC responsabil cu comunicarea cu respectiva baz˘ de date. Acest a lucru presupune ˆ arcarea ˆ memorie a clasei ce implementeaz˘ driver-ul ¸i ınc˘ ın a s poate fi realizat˘ ˆ mai multe modalit˘¸i. a ın at a. Folosirea clasei DriverManager: DriverManager.registerDriver(new TipDriver()); b. Folosirea metodei Class.forName ce apeleaz˘ ClassLoader-ul ma¸inii a s virtuale: Class.forName("TipDriver"); Class.forName("TipDriver").newInstance(); c. Setarea propriet˘¸ii sistem jdbc.drivers, care poate fi realizat˘ ˆ dou˘ at a ın a feluri: – De la linia de comand˘: a java -Djdbc.drivers=TipDriver Aplicatie – Din program: System.setProperty("jdbc.drivers", "TipDriver"); Folosind aceast˘ metod˘, specificarea mai multor drivere se face separˆnd a a a numele claselor cu punct ¸i virgul˘. s a Dac˘ sunt ˆ a ınregistrate mai multe drivere, ordinea de precedent˘ ˆ alegerea ¸a ın driverului folosit la crearea unei noi conexiuni este: 1) Driverele ˆ ınregistrate folosind proprietatea jdbc.drivers la initializarea ¸ ma¸inii virtuale ce va rula procesul. s 2) Driverele ˆ ınregistrate dinamic din aplicatie. ¸
  • 428.
    ˘ 15.2. CONECTAREA LAO BAZA DE DATE 427 15.2.2 Specificarea unei baze de date O dat˘ ce un driver JDBC a fost ˆ a ınregistrat, acesta poate fi folosit la stabilirea unei conexiuni cu o baz˘ de date. Avˆnd ˆ vedere faptul ca pot exista mai a a ın multe drivere ˆ arcate ˆ memorie, trebuie s˘ avem posibilitea de a specifica ınc˘ ın a pe lˆng˘ un identificator al bazei de date ¸i driverul ce trebuie folosit. Aceasta a a s se realizeaz˘ prin intermediul unei adrese specifice, numit˘ JDBC URL, ce a a are urm˘torul format: a jdbc:sub-protocol:identificator Cˆmpul sub-protocol denume¸te tipul de driver ce trebuie folosit pentru a s realizarea conexiunii ¸i poate fi odbc, oracle, sybase, db2 ¸i a¸a mai departe. s s s Identificatorul bazei de date este un indicator specific fiec˘rui driver core- a spunz˘tor bazei de date cu care aplicatia dore¸te s˘ interactioneze. In functie a ¸ s a ¸ ¸ de tipul driver-ului acest identificator poate include numele unei ma¸ini s gazd˘, un num˘r de port, numele unui fi¸ier sau al unui director, etc., ca a a s ˆ exemplele de mai jos: ın jdbc:odbc:test jdbc:oracle:thin@persistentjava.com:1521:test jdbc:sybase:test jdbc:db2:test Subprotocolul odbc este un caz specical, ˆ sensul c˘ permite specificarea ın a ˆ cadrul URL-ului a unor atribute ce vor fi realizate la crearea unei conexi- ın uni. Sintaxa completa subprotocolului odbc este: jdbc:odbc:identificator[;atribut=valoare]* jdbc:odbc:test jdbc:odbc:test;CacheSize=20;ExtensionCase=LOWER jdbc:odbc:test;UID=duke;PWD=java La primirea unui JDBC URL, DriverManager-ul va parcurge lista driverelor ˆ ınregistrate ˆ memorie, pˆna cˆnd unul dintre ele va recunoa¸te URL-ul re- ın a a s spectiv. Dac˘ nu exista nici unul potrivit, atunci va fi lansata o exceptie de a ¸ tipul SQLException, cu mesajul "no suitable driver".
  • 429.
    428 CAPITOLUL 15. LUCRUL CU BAZE DE DATE 15.2.3 Tipuri de drivere Tipurile de drivere existente ce pot fi folosite pentru realizarea unei conexiuni prin intermediul JDBC se ˆ ımpart ˆ urm˘toarele categorii: ın a Tip 1. JDBC-ODBC Bridge Acest tip de driver permite conectarea la o baz˘ de date care a fost a ˆ ınregistrat˘ ˆ prealabil ˆ ODBC. ODBC (Open Database Conectivity) a ın ın reprezint˘ o modalitate de a uniformiza accesul la baze de date, asociind a acestora un identificator DSN (Data Source Name) ¸i diver¸i parametri nece- s s sari conect˘rii. Conectarea efectiv˘ la baza de date se va face prin intermediul a a acestui identificator, driver-ul ODBC efectuˆnd comunicarea cu driverul na- a tiv al bazei de date. De¸i simplu de utilizat, solutia JDBC-ODBC nu este portabil˘ ¸i comuni- s ¸ as carea cu baza de date sufer˘ la nivelul vitezei de executie datorit˘ multiplelor a ¸ a redirect˘ri ˆ a ıntre drivere. De asemenea, atˆt ODBC-ul cˆt ¸i driver-ul nativ a a s trebuie s˘ existe pe ma¸ina pe care ruleaz˘ aplicatia. a s a ¸ Clasa Java care descrie acest tip de driver JDBC este: sun.jdbc.odbc.JdbcOdbcDriver ¸i este inclus˘ ˆ distributia standard J2SDK. Specificarea bazei de date se s a ın ¸ face printr-un URL de forma: jdbc:odbc:identificator unde identif icator este profilul (DSN) creat bazei de date ˆ ODBC. ın Tip 2. Driver JDBC - Driver nativ
  • 430.
    ˘ 15.2. CONECTAREA LAO BAZA DE DATE 429 Acest tip de driver transform˘ cererile JDBC direct ˆ apeluri c˘tre a ın a driverul nativ al bazei de date, care trebuie instalat ˆ prealabil. Clase Java ın care implementeaz˘ astfel de drivere pot fi procurate de la produc˘torii de a a SGBD-uri, distributia standard J2SDK neincluzˆnd nici unul. ¸ a Tip 3. Driver JDBC - Server Acest tip de driver transform˘ cererile JDBC folosind un protocol de retea a ¸ independent, acestea fiind apoi transormate folosind o aplicatie server ˆ ¸ ıntr-un protocol specfic bazei de date. Introducerea serverului ca nivel intermediar aduce flexibilitate maxim˘ ˆ sensul c˘ vor putea fi realizate conexiuni cu a ın a diferite tipuri de baze, f˘r˘ nici o modificare la nivelul clientului. Protocolul aa folosit este specific fiec˘rui produc˘tor. a a Tip 4. Driver JDBC nativ
  • 431.
    430 CAPITOLUL 15. LUCRUL CU BAZE DE DATE Acest tip de driver transform˘ cererile JDBC direct ˆ cereri c˘tre baza a ın a de date folosind protocolul de retea al acesteia. Aceast˘ solutie este cea mai ¸ a ¸ rapid˘, fiind preferat˘ la dezvoltarea aplicatiilor care manevreaz˘ volume a a ¸ a mari de date ¸i viteza de executie este critic˘. Drivere de acest tip pot fi s ¸ a procurate de la diver¸i produc˘tori de SGBD-uri. s a 15.2.4 Realizarea unei conexiuni Metoda folosit˘ pentru realizarea unei conexiuni este getConnection din a clasa DriverManager ¸i poate avea mai multe forme: s Connection c = DriverManager.getConnection(url); Connection c = DriverManager.getConnection(url, username, password); Connection c = DriverManager.getConnection(url, dbproperties); Stabilirea unei conexiuni folosind driverul JDBC-ODBC String url = "jdbc:odbc:test" ; // sau url = "jdbc:odbc:test;UID=duke;PWD=java" ; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(ClassNotFoundException e) { System.err.print("ClassNotFoundException: " + e) ; return ; } Connection con ; try { con = DriverManager.getConnection(url, "duke", "java"); } catch(SQLException e) { System.err.println("SQLException: " + e);
  • 432.
    15.3. EFECTUAREA DESECVENTE SQL ¸ 431 } finally { try{ con.close ; } catch(SQLException e) { System.err.println(SQLException: " + e) ; } } Stabilirea unei conexiuni folosind un driver MySql Folosirea diferitelor tipuri de drivere implic˘ doar schimbarea numelui clasei a ce reprezint˘ driverul ¸i a modalit˘¸ii de specificare a bazei de date. a s at String url = "jdbc:mysql://localhost/test" ; // sau url = "jdbc:mysql://localhost/test?user=duke&password=java"; try { Class.forName("com.mysql.jdbc.Driver") ; } catch(ClassNotFoundException e) { ... O conexiune va fi folosit˘ pentru: a • Crearea de secvente SQL utilizate pentru interogarea sau actualizarea ¸ bazei. • Aflarea unor informatii legate de baza de date (meta-date). ¸ De asemenea, clasa Connection asigur˘ facilit˘¸i pentru controlul tranzactiilor a at ¸ din memorie c˘tre baza de date prin metodele commit, rollback, setAu- a toCommit. Inchiderea unei conexiuni se realizeaz˘ prin metoda close. a 15.3 Efectuarea de secvente SQL ¸ O dat˘ facut˘ conectarea cu metoda DriverManager.getConection, se poate a a folosi obiectul Connection rezultat pentru a se crea obiecte de tip State- ment,PreparedStatement sau CallableStatement cu ajutorul c˘rora a putem trimite secvente SQL c˘tre baza de date. Cele mai uzuale comenzi ¸ a SQL sunt cele folosite pentru: • Interogarea bazei de date: SELECT
  • 433.
    432 CAPITOLUL 15. LUCRUL CU BAZE DE DATE • Actualizarea datelor: INSERT, UPDATE, DELETE • Actualizarea structurii: CREATE, ALTER, DROP - acestea mai sunt nu- mite instructiuni DDL (Data Definition Language) ¸ • Apelarea unei proceduri stocate: CALL Dup˘ cum vom vedea, obtinerea ¸i prelucrarea rezultatelor unei interog˘ri a ¸ s a este realizat˘ prin intermediul obiectelor de tip ResultSet. a 15.3.1 Interfata Statement ¸ Interfata Statement ofer˘ metodele de baz˘ pentru trimiterea de secvente ¸ a a ¸ SQL c˘tre baza de date ¸i obtinerea rezultatelor, celelalte dou˘ interfete: a s ¸ a ¸ PreparedStatement ¸i CallableStatement fiind derivate din aceasta. s Crearea unui obiect Statement se realizeaz˘ prin intermediul metodei a createStatement a clasei Connection, f˘r˘ nici un argument: aa Connection con = DriverManager.getConnection(url); Statement stmt = con.createStatement(); Executia unei secvente SQL poate fi realizat˘ prin intermediul a trei ¸ ¸ a metode: 1. executeQuery Este folosit˘ pentru realizarea de interog˘ri de tip SELECT. Metoda returneaz˘ a a a un obiect de tip ResultSet ce va contine sub o form˘ tabelar˘ rezultatul ¸ a a interog˘rii. a String sql = "SELECT * FROM persoane"; ResultSet rs = stmt.executeQuery(sql); 2. executeUpdate Este folosit˘ pentru actualizarea datelor (INSERT, UPDATE, DELETE) sau a a structurii bazei de date (CREATE, ALTER, DROP). Metoda va returna un ˆ ıntreg ce semnific˘ num˘rul de linii afectate de operatiunea de actualizare a a a ¸ datelor, sau 0 ˆ cazul unei instructiuni DDL. ın ¸
  • 434.
    15.3. EFECTUAREA DESECVENTE SQL ¸ 433 String sql = "DELETE FROM persoane WHERE cod > 100"; int linii = stmt.executeUpdate(sql); // Nr de articole care au fost afectate (sterse) sql = "DROP TABLE temp"; stmt.executeUpdate(sql); // returneaza 0 3. execute Aceast˘ metod˘ va fi folosit˘ doar dacˆ este posibil ca rezultatul unei in- a a a a terog˘ri s˘ fie format din dou˘ sau mai multe obiecte de tip ResultSet sau a a a rezultatul unei actualiz˘ri s˘ fie format din mai mule valori, sau o combinatie a a ¸ ˆ ıntre aceste cazuri. Aceast˘ situatie, de¸i mai rar˘, este posibil˘ atunci cˆnd a ¸ s a a a sunt executate proceduri stocate sau secvente SQL cunoscute abia la momen- ¸ tul executiei, programatorul ne¸tiind deci dac˘ va fi vorba de o actualizare ¸ s a a datelor sau a structurii. Metoda ˆ ıntoarce true dac˘ rezultatul obtinut a ¸ este format din obiecte de tip ResultSet ¸i false dac˘ e format din ˆ s a ıntregi. In functie de aceasta, pot fi apelate metodele: getResultSet sau getUp- ¸ dateCount pentru a afla efectiv rezultatul comenzii SQL. Pentru a prelua toate rezultatele va fi apelat˘ metoda getMoreResults, dup˘ care vor fi a a apelate din nou metodele amintite, pˆn˘ la obtinerea valorii null, respectiv a a ¸ −1. Secventa complet˘ de tratare a metodei execute este prezentat˘ mai ¸ a a jos: String sql = "comanda SQL necunoscuta"; stmt.execute(sql); while(true) { int rowCount = stmt.getUpdateCount(); if(rowCount > 0) { // Este o actualizare datelor System.out.println("Linii afectate = " + rowCount); stmt.getMoreResults(); continue; } if(rowCount = 0) { // Comanda DDL sau nici o linie afectata System.out.println("Comanda DDL sau 0 actualizari");
  • 435.
    434 CAPITOLUL 15. LUCRUL CU BAZE DE DATE stmt.getMoreResults(); continue; } // rowCount este -1 // Avem unul sau mai multe ResultSet-uri ResultSet rs = stmt.getResultSet(); if(rs != null) { // Proceseaza rezultatul ... stmt.getMoreResults(); continue; } // Nu mai avem nici un rezultat break; } Folosind clasa Statement, ˆ cazul ˆ care dorim s˘ introducem valorile ın ın a unor variabile ˆ ıntr-o secvent˘ SQL, nu avem alt˘ solutie decˆt s˘ cre˘m un ¸a a ¸ a a a ¸ir de caractere compus din instructiuni SQL ¸i valorile variabilelor: s ¸ s int cod = 100; String nume = "Popescu"; String sql = "SELECT * FROM persoane WHERE cod=" + cod + " OR nume=’" + nume + "’"; ResultSet rs = stmt.executeQuery(sql); 15.3.2 Interfata PreparedStatement ¸ Interfata PreparedStatement este derivat˘ din Statement, fiind diferit˘ de ¸ a a aceasta ˆ urm˘toarele privinte: ın a ¸ • Instantele de tip PreparedStatement contin secvente SQL care au fost ¸ ¸ ¸ deja compilate (sunt ”preg˘tite”). a • O secvent˘ SQL specificat˘ unui obiect PreparedStatement poate s˘ ¸a a a aib˘ unul sau mai multi parametri de intrare, care vor fi specificati a ¸ ¸ prin intermediul unui semn de ˆ ıntrebare (”?”) ˆ locul fiec˘ruia dintre ın a
  • 436.
    15.3. EFECTUAREA DESECVENTE SQL ¸ 435 ei. Inainte ca secventa SQL s˘ poat˘ fi executat˘ fiec˘rui parametru ¸ a a a a de intrare trebuie s˘ i se atribuie o valoare, folosind metode specifice a acestei clase. Executia repetat˘ a aceleia¸i secvente SQL, dar cu parametri diferiti, va ¸ a s ¸ ¸ fi ˆ general mai rapid˘ dac˘ folosim PreparedStatement, deoarece nu mai ın a a trebuie s˘ cre˘m cˆte un obiect de tip Statement pentru fiecare apel SQL, ci a a a refolosim o singur˘ instant˘ precompilat˘ furnizˆndu-i doar alte argumente. a ¸a a a Crearea unui obiect de tip PreparedStatement se realizeaz˘ prin inter- a mediul metodei prepareStatement a clasei Connection, specificˆn ca ar- a gument o secvent˘ SQL ce contine c˘te un semn de ˆ ¸a ¸ a ıntrebare pentru fiecare parametru de intrare: Connection con = DriverManager.getConnection(url); String sql = "UPDATE persoane SET nume=? WHERE cod=?"; Statement pstmt = con.prepareStatement(sql); Obiectul va pstmt contine o comand˘ SQL precompilat˘ care este trimis˘ ¸ a a a imediat c˘tre baza de date, unde va a¸tepta parametri de intrare pentru a a s putea fi executat˘.a Trimiterea parametrilor se realizeaz˘ prin metode de tip setXXX, a unde XXX este tipul corespunz˘tor parametrului, iar argumentele metodei a sunt num˘rul de ordine al parametrului de intrare (al semnului de ˆ a ıntrebare) ¸i valoarea pe care dorim s˘ o atribuim. s a pstmt.setString(1, "Ionescu"); pstmt.setInt(2, 100); Dup˘ stabilirea parametrilor de intrare secventa SQL poate fi executat˘. a ¸ a Putem apoi stabili alte valori de intrare ¸i refolosi obiectul PreparedStatement s pentru executii repetate ale comenzii SQL. Este ˆ a posibil ca SGBD-ul ¸ ıns˘ folosit s˘ nu suporte acest tip de operatiune ¸i s˘ nu retin˘ obiectul pre- a ¸ s a ¸ a compilat pentru executii ulterioare. In aceast˘ situatie folosirea interfetei ¸ a ¸ ¸ PreparedStatement ˆ loc de Statement nu va ˆ ın ımbun˘t˘¸i ˆ nici un fel a at ın performanta codului, din punctul de vedere al vitezei de executie a acestuia. ¸ ¸ Executia unei secvente SQL folosind un obiect PreparedStatement se ¸ ¸ realizeaz˘ printr-una din metodele executeQuery, executeUpdate sau a execute, semnificatiile lor fiind acelea¸i ca ¸i ˆ cazul obiectelor de tip ¸ s s ın Statement, cu singura deosebire c˘ ˆ cazul de fat˘ ele nu au nici un ar- a ın ¸a gument.
  • 437.
    436 CAPITOLUL 15. LUCRUL CU BAZE DE DATE String sql = "UPDATE persoane SET nume=? WHERE cod=?"; Statement pstmt = con.prepareStatement(sql); pstmt.setString(1, "Ionescu"); pstmt.setInt(2, 100); pstmt.executeUpdate(); pstmt.setString(1, "Popescu"); pstmt.setInt(2, 200); pstmt.executeUpdate(); sql = "SELECT * from persoane WHERE cod >= ?"; pstmt = con.prepareStatement(sql); pstmt.setInt(1, 100); ResultSet rs = pstmt.executeQuery(); Fiec˘rui tip Java ˆ corespunde un tip generic SQL. Este responsabilitatea a ıi programatorului s˘ se asigure c˘ folose¸te metoda adecvat˘ de tip setXXX la a a s a stabilirea valorii unui parametru de intrare. Lista tuturor tipurilor generice disponibile, numite ¸i tipuri JDBC, este definit˘ de clasa Types, prin con- s a stantelor declarate de aceasta. Metoda setObject permite specificarea unor valori pentru parametrii de intrare, atunci cˆnd dorim s˘ folosim maparea a a implicit˘ ˆ a ıntre tipurile Java ¸i cele JDBC sau atunci cˆnd dorim s˘ preciz˘m s a a a explicit un tip JDBC. pstmt.setObject(1, "Ionescu", Types.CHAR); pstmt.setObject(2, 100, Types.INTEGER); // sau doar pstmt.setObject(2, 100); Folosind metoda setNull putem s˘ atribuim unui parametru de intrare a valoare SQL NULL, trebuind ˆ a s˘ specific˘m ¸i tipul de date al coloanei ˆ ıns˘ a a s ın care vom scrie aceast˘ valoare. Acela¸i lucru poate fi realizat cu metode de a s tipul setXXX dac˘ argumentul folosit are valoarea null. a pstmt.setNull(1, Types.CHAR); pstmt.setInt(2, null); Cu ajutorul metodelor setBytes sau setString avem posibilitatea de a specifica date de orice dimensiuni ca valori pentru anumite articole din baza de date. Exist˘ ˆ a situatii cˆnd este de preferat ca datele de mari dimensi- a ıns˘ ¸ a uni s˘ fie transferate pe ”buc˘¸i” de o anumit˘ dimensiune. Pentru a realiza a at a
  • 438.
    15.3. EFECTUAREA DESECVENTE SQL ¸ 437 acest lucru API-ul JDBC pune la dispozitie metodele setBinaryStream, ¸ setAsciiStream ¸i setUnicodeStream care ata¸eaz˘ un flux de intrare pe s s a octeti, caractere ASCII, respectiv UNICODE, unui parametru de intrare. Pe ¸ m˘sur˘ ce sunt citite date de pe flux, ele vor fi atribuite parametrului. Exem- a a plul de mai jos ilustreaz˘ acest lucru, atribuind coloanei continut continutul a ¸ unui anumit fi¸ier: s File file = new File("date.txt"); int fileLength = file.length(); InputStream fin = new FileInputStream(file); java.sql.PreparedStatement pstmt = con.prepareStatement( "UPDATE fisiere SET continut = ? WHERE nume = ’date.txt’"); pstmt.setUnicodeStream (1, fin, fileLength); pstmt.executeUpdate(); La executia secventei, fluxul de intrare va fi apelat repetat pentru a ¸ ¸ furniza datele ce vor fi scrise ˆ coloana continut a articolului specificat. ın Observati c˘ este necesar ˆ a s˘ ¸tim dinainte dimensiunea datelor ce vor fi ¸ a ın˘ a s scrise, acest lucru fiind solicitat de unele tipuri de baze de date. 15.3.3 Interfata CallableStatement ¸ Interfata CallableStatement este derivat˘ din PreparedStatement, instantele ¸ a ¸ de acest tip oferind o modalitate de a apela o procedur˘ stocat˘ ˆ a a ıntr-o baz˘ a de date, ˆ ıntr-o manier˘ standar pentru toate SGBD-urile. a Crearea unui obiect CallableStatement se realizeaz˘ prin metoda pre- a pareCall a clasei Connection: Connection con = DriverManager.getConnection(url); CallableStatement cstmt = con.prepareCall( "{call proceduraStocata(?, ?)}"); Trimiterea parametrilor de intrare se realizeaz˘ ˆa ıntocmai ca la PreparedStatement, cu metode de tip setXXX. Dac˘ procedura are ¸i parametri de ie¸ire (valori a s s returnate), ace¸tia vor trebui ˆ s ınregistrati cu metoda registerOutParame- ¸ ter ˆ ınainte de executia procedurii. Obtinerea valorilor rezultate ˆ parametrii ¸ ¸ ın de ie¸ie se va face cu metode de tip getXXX. s CallableStatement cstmt = con.prepareCall(
  • 439.
    438 CAPITOLUL 15. LUCRUL CU BAZE DE DATE "{call calculMedie(?)}"); cstmt.registerOutParameter(1, java.sql.Types.FLOAT); cstmt.executeQuery(); float medie = cstmt.getDouble(1); Este posibil ca un parametru de intrare s˘ fie ¸i parametru de ie¸ire. In a s s acest caz el trebuie s˘ primeasc˘ o valoare cu setXXX ¸i, de asemenea, va fi a a s ˆ ınregistrat cu registerOutParameter, tipurile de date specificate trebuind s˘ coincid˘. a a 15.3.4 Obtinerea ¸i prelucrarea rezultatelor ¸ s 15.3.5 Interfata ResultSet ¸ In urma executie unei interog˘ri SQL rezultatul va fi reprezentat printr-un ¸ a obiect de tip ResultSet, ce va contine toate liniile ce satisfac conditiile ¸ ¸ impuse de comanda SQL. Forma general˘ a unui ResultSet este tabelar˘, a a avˆnd un num˘r de coloane ¸i de linii, functie de secventa executat˘. De a a s ¸ ¸ a asemenea, obiectul va contine ¸i meta-datele interog˘rii cum ar fi denumirele ¸ s a coloanelor selectate, num˘rul lor, etc. a Statement stmt = con.createStatement(); String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); Rezultatul interog˘rii de mai sus va fi obiectul rs cu urm˘toarea structur˘: a a a cod nume 100 Ionescu 200 Popescu Pentru a extrage informatiile din aceast˘ structur˘ va trebui s˘ parcurgem ¸ a a a tabelul linie cu linie ¸i din fiecare s˘ extragem valorile de pe coloane. Pentru s a acest lucru vom folosi metode de tip getXXX, unde XXX este tipul de dat˘ al unei coloane iar argumentul primit indic˘ fie num˘rul de ordine din a a a cadrul tabelului, fie numele acestuia. Coloanele sunt numerotate de la stˆnga a la dreapta, ˆ ıncepˆnd cu 1. In general, folosirea indexului coloanei ˆ loc de a ın numele s˘u va fi mai eficient˘. De asemenea, pentru maxim˘ portabilitate a a a se recomand˘ citirea coloanelor ˆ ordine de la stˆnga la dreapta ¸i fiecare a ın a s citire s˘ se fac˘ o singur˘ dat˘. a a a a
  • 440.
    15.3. EFECTUAREA DESECVENTE SQL ¸ 439 Un obiect ResultSet folose¸te un cursor pentru a parcurge articolele s rezultate ˆ urma unei interog˘ri. Initial acest cursor este pozitionat ˆ ın a ¸ ¸ ınaintea primei linii, fiecare apel al metodei next determinˆnd trecerea la urm˘toarea a a linie. Deoarece next returneaz˘ false cˆnd nu mai sunt linii de adus, uzual a a va fi folosit˘ o bucl˘ while-loop petru a itera prin articolele tabelului: a a String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { int cod = r.getInt("cod"); String nume = r.getString("nume"); /* echivalent: int cod = r.getInt(1); String nume = r.getString(2); */ System.out.println(cod + ", " + nume); } Implicit, un tabel de tip ResultSet nu poate fi modificat iar cursorul asociat nu se deplaseaz˘ decˆt ˆ a a ınainte, linie cu linie. A¸adar, putem itera s prin rezultatul unei interog˘ri o singur˘ dat˘ ¸i numai de la prima la ultima a a as linie. Este ˆ a posibil s˘ cre˘m ResultSet-uri care s˘ permit˘ modificarea ıns˘ a a a a sau deplasarea ˆ ambele sensuri. Exemplul urm˘tor va folosi un cursor care ın a este modificabil ¸i nu va reflecta schimb˘rile produse de alti utilizatori dup˘ s a ¸ a crearea sa: Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); String sql = "SELECT cod, nume FROM persoane"; ResultSet rs = stmt.executeQuery(sql); Dac˘ un ResultSet folose¸te un cursor modificabil ¸i care poate naviga a s s ˆ ambele sensuri, atunci are la dispozitie o serie de metode ce se bazeaz˘ pe ın ¸ a acest suport: • absolute - Deplaseaz˘ cursorul la o anumit˘ linie specificat˘ absolut; a a a • updateXXX - Actualizeaz˘ valoarea unei coloane din linia curent˘, a a unde XXX este un tip de date.
  • 441.
    440 CAPITOLUL 15. LUCRUL CU BAZE DE DATE • updateRow - Transfer˘ actualiz˘rile f˘cute liniei ˆ baza de date. a a a ın • moveToInsertRow - deplaseaz˘ cursorul la o linie spceial˘, numit˘ a a a linie nou˘, utilizate˘ pentru a introduce noi articole ˆ baza de date. a a ın Linia curent˘ anterioar˘ a cursorului va fi memorat˘ pentru a se putea a a a reveni la ea. • insertRow - insereaz˘ articolul din zona linie nou˘ ˆ baza de date; a a ın cursorul trebuie s˘ fie pozitionat le linia nou˘ la executia acestei operatiuni. a ¸ a ¸ ¸ • moveToCurrentRow - revine la linia curent˘ din tabel. a • deleteRow - ¸terge linia curent˘ din tabel ¸i din baza de date; nu s a s poate fi apelat˘ cˆnd cursorul este ˆ modul linie nou˘. a a ın a Nu toate sistemele de gestiune a bazelor de date ofer˘ suport pentru a folosirea cursoarelor care pot fi modificate. Pentru a determina dac˘ baza de a date permite acest lucru pot fi utilizate metodele supportsPositionedUp- date ¸i supportsPositionedDelete ale clasei DatabaseMetaData. In cazul s ˆ care acest lucru este permis, este responsabilitatea driver-ului bazei de ın date s˘ asigure rezolvarea problemelor legate de actualizarea concurent˘ a a a unui cursor, astfel ˆ at s˘ nu apar˘ anomalii. ıncˆ a a 15.3.6 Exemplu simplu In continuare vom da un exemplul simplu de utilizare a claselor de baz˘ a mentionate anterior. Programul va folosi o baz˘ de date MySql, ce contine ¸ a ¸ un tabel numit persoane, avˆnd coloanele: cod, nume ¸i salariu. Scriptul a s SQL de creare a bazei este: create table persoane(cod integer, nume char(50), salariu double); Aplicatia va goli tabelul cu persoane, dup˘ care va ad˘uga aleator un ¸ a a num˘r de articole, va efectua afi¸area lor ¸i calculul mediei salariilor. a s s Listing 15.1: Exemplu simplu de utilzare JDBC import java . sql .*; public class TestJdbc { public static void main ( String [] args ) {
  • 442.
    15.3. EFECTUAREA DESECVENTE SQL ¸ 441 String url = " jdbc : mysql :// localhost / test " ; try { Class . forName ( " com . mysql . jdbc . Driver " ) ; } catch ( Cl as sNo tF oun dE x c e p t i o n e ) { System . out . println ( " Eroare incarcare driver ! n " + e ) ; return ; } try { Connection con = DriverManager . getConnection ( url ) ; // Golim tabelul persoane String sql = " DELETE FROM persoane " ; Statement stmt = con . createStatement () ; stmt . executeUpdate ( sql ) ; // Adaugam un numar de persoane generate aleator // Tabelul persoane are coloanele ( cod , nume , salariu ) int n = 10; sql = " INSERT INTO persoane VALUES (? , ? , ?) " ; PreparedStatement pstmt = con . prepareStatement ( sql ) ; for ( int i =0; i < n ; i ++) { int cod = i ; String nume = " Persoana " + i ; double salariu = 100 + Math . round ( Math . random () * 900) ; // salariul va fi intre 100 si 1000 pstmt . setInt (1 , cod ) ; pstmt . setString (2 , nume ) ; pstmt . setDouble (3 , salariu ) ; pstmt . executeUpdate () ; } // Afisam persoanele ordonate dupa salariu sql = " SELECT * FROM persoane ORDER BY salariu " ; ResultSet rs = stmt . executeQuery ( sql ) ; while ( rs . next () ) System . out . println ( rs . getInt ( " cod " ) + " , " + rs . getString ( " nume " ) + " , " + rs . getDouble ( " salariu " ) ) ; // Calculam salariul mediu sql = " SELECT avg ( salariu ) FROM persoane " ; rs = stmt . executeQuery ( sql ) ; rs . next () ;
  • 443.
    442 CAPITOLUL 15. LUCRUL CU BAZE DE DATE System . out . println ( " Media : " + rs . getDouble (1) ) ; // Inchidem conexiunea con . close () ; } catch ( SQLException e ) { e . printStackTrace () ; } } } 15.4 Lucrul cu meta-date 15.4.1 Interfata DatabaseMetaData ¸ Dup˘ realizarea unui conexiuni la o baz˘ de date, putem apela metoda get- a a MetaData pentru a afla diverse informatii legate de baza respectiv˘, a¸a ¸ a s numitele meta-date (”date despre date”); Ca rezult al apelului metodei, vom obtine un obiect de tip DatabaseMetaData ce ofer˘ metode pentru deter- ¸ a minarea tabelelor, procedurilor stocate, capabilit˘¸ilor conexiunii, gramaticii at SQL suportate, etc. ale bazei de date. Programul urm˘tor afi¸eaz˘ numele tuturor tabelelor dintr-o baz˘ de dat a s a a ˆ ınregistrat˘ ˆ ODBC. a ın Listing 15.2: Folosirea interfetei DatabaseMetaData ¸ import java . sql .*; public class TestMetaData { public static void main ( String [] args ) { String url = " jdbc : odbc : test " ; try { Class . forName ( " sun . jdbc . odbc . JdbcOdbcDriver " ) ; } catch ( Cl as sNo t F o u n d E x c e p t i o n e ) { System . out . println ( " Eroare incarcare driver ! n " + e ) ; return ; } try { Connection con = DriverManager . getConnection ( url ) ; DatabaseMetaData dbmd = con . getMetaData () ; ResultSet rs = dbmd . getTables ( null , null , null , null ) ;
  • 444.
    15.4. LUCRUL CUMETA-DATE 443 while ( rs . next () ) System . out . println ( rs . getString ( " TABLE_NAME " ) ) ; con . close () ; } catch ( SQLException e ) { e . printStackTrace () ; } } } 15.4.2 Interfata ResultSetMetaData ¸ Meta-datele unui ResultSet reprezint˘ informatiile despre rezultatul continut a ¸ ¸ ˆ acel obiect cum ar fi num˘rul coloanelor, tipul ¸i denumirile lor, etc. Aces- ın a s tea sunt obtinute apelˆnd metoda getMetaData pentru ResultSet-ul re- ¸ a spectiv, care va returna un obiect de tip ResultSetMetaData ce poate fi apoi folosit pentru extragerea informatiilor dorite. ¸ ResultSet rs = stmt.executeQuery("SELECT * FROM tabel"); ResultSetMetaData rsmd = rs.getMetaData(); // Aflam numarul de coloane int n = rsmd.getColumnCount(); // Aflam numele coloanelor Sring nume[] = new String[n+1]; for(int i=1; i<=n; i++) nume[i] = rsmd.getColumnName(i);
  • 445.
    444 CAPITOLUL 15. LUCRUL CU BAZE DE DATE
  • 446.
    Capitolul 16 Lucrul dinamiccu clase 16.1 Inc˘rcarea claselor ˆ memorie a ın Dup˘ cum ¸tim executia unei aplicatii Java este realizat˘ de c˘tre ma¸ina a s ¸ ¸ a a s virtual˘ Java (JVM), aceasta fiind responsabil˘ cu interpretarea codului de a a octeti rezultat ˆ urma compil˘rii. Spre deosebire de alte limbaje de progra- ¸ ın a mare cum ar fi C sau C++, un program Java compilat nu este descris de un fi¸ier executabil ci de o multime de fi¸iere cu extensia .class corespunz˘toare s ¸ s a fiec˘rei clase a programului. In plus, aceste clase nu sunt ˆ arcate toate ˆ a ın˘ ın memorie la pornirea aplicatiei, ci sunt ˆ arcate pe parcursul executie acesteia ¸ ın˘ ¸ atunci cˆnd este nevoie de ele, momentul efectiv ˆ care se realizeaz˘ acest a ın a lucru depinzˆnd de implementarea ma¸inii virtuale. a s Ciclul de viat˘ al unei clase are a¸adar urm˘toarele etape: ¸a s a 1. Inc˘rcarea - Este procesul reg˘sirii reprezent˘rii binare a unei clase a a a (fi¸ierul .class) pe baza numelui complet al acestuia ¸i ˆ arcarea aces- s s ınc˘ teia ˆ memorie. In urma acestui proces, va fi instantiat un obiect de ın ¸ tip java.lang.Class, corespunz˘tor clasei respective. Operatiunea de a ¸ ˆ arcare a unei clase este realizat˘ la un moment ce precede prima ınc˘ a utilizare efectiv˘ a sa. a 2. Editarea de leg˘turi - Specific˘ incorporarea noului tip de date ˆ JVM a a ın pentru a putea fi utlizat. 3. Initializarea - Const˘ ˆ executia blocurilor statice de initializare ¸i ¸ a ın ¸ ¸ s initializarea variabilelor de clas˘. ¸ a 445
  • 447.
    446 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE 4. Desc˘rcarea - Atunci cˆnd nu mai exist˘ nici o referint˘ de tipul clasei a a a ¸a respective, obiectul de tip Class creat va fi marcat pentru a fi eliminat din memorie de c˘tre garbage collector. a Inc˘rcarea claselor unei aplicatii Java ˆ memorie este realizat˘ prin in- a ¸ ın a termediul unor obiecte pe care le vom numi generic class loader. Acestea sunt de dou˘ tipuri: a 1. Class loader-ul primordial (eng. bootstrap) - Reprezint˘ o parte inte- a grant˘ a ma¸inii virtuale, fiind responsabil cu ˆ arcarea claselor stan- a s ınc˘ dard din distributia Java. ¸ 2. Class loader-e proprii - Acestea nu fac parte intrinsec˘ din JVM ¸i a s sunt instante ale clasei java.lang.ClassLoader. Aceasta este o clas˘ ¸ a abstract˘, tipul efectiv al obiectului fiind a¸adar derivat din aceasta. a s Dup˘ cum vom vedea, la executia unui program Java vor fi create implicit a ¸ dou˘ obiecte de tip ClassLoader pentru ˆ arcarea ˆ memorei a claselor a ınc˘ ın proprii ale aplicatiei. Exist˘ ˆ a posibilitarea de a crea noi tipuri derivate din ¸ a ıns˘ ClassLoader specializate pentru ˆ arcarea claselor conform unor specificatii ınc˘ ¸ anume care s˘ realizeze diverse optimiz˘ri. Astfel, ˆ arcarea unei clase poate a a ınc˘ determina ˆ arcarea unor altor clase care sigur vor fi folosite ˆ ınc˘ ımpreun˘ cu a prima, sau a unor resurse ce sunt necesare function˘rii acesteia, etc. ¸ a Incepˆnd cu versiunea 1.2 de Java, a fost introdus un model de tip delegat, a ˆ care class loader-ele sunt dispuse ierarhic ˆ ın ıntr-un arbore, r˘d˘cina acestuia a a fiind class loader-ul primordial. Fiecare instanta de tip ClassLoader va avea ¸ a¸adar un p˘rinte (evident, mai putin r˘d˘cina), acesta fiind specificat la s a ¸ a a crearea sa. In momentul cˆnd este solicitat˘ ˆ arcarea unei clase, un class- a a ınc˘ loader poate delega ˆ primul rˆnd operatiunea de ˆ arcare p˘rintelui s˘u ın a ¸ ınc˘ a a care va delega la rˆndul s˘u cererea mai departe pˆn˘ la class loader-ul a a a a primordial sau pˆn˘ unul din ace¸tia reu¸e¸te s˘ o ˆ a a s s s a ıncarce. Abia ˆ cazul ˆ ın ın care nici unul din ace¸tia nu a reu¸it, va ˆ s s ıncerca s˘ execute operatiunea de a ¸ ˆ arcare a clasei. Dac˘ nici ea nu va reu¸i, va fi aruncat˘ o exceptie de tipul ınc˘ a s a ¸ ClassNotFoundException. De¸i acest comportament nu este obligatoriu, s ˆ multe situatii el este de preferat, pentru a minimiza ˆ arcarea aceleia¸i ın ¸ ınc˘ s clase de mai multe ori, folosind class loader-e diferite.
  • 448.
    16.1. INCARCAREA CLASELORˆ MEMORIE ˘ IN 447 Implicit, Java 2 JVM ofer˘ trei class loader-e, unul primordial ¸i dou˘ a s a proprii, cunoscute sub numele de: • Boostrap Class Loader - Class loader-ul primordial. Acesta este re- sponsabil cu ˆ arcarea claselor din distributia Java standard (cele din ınc˘ ¸ pachetele java.*, javax.*, etc.). • Extension Class Loader - Utilizat pentru ˆ arcarea claselor din direc- ınc˘ toarele extensiilor JRE. • System Class Loader - Acesta este responsabil cu ˆ arcarea claselor ınc˘ proprii aplicatiilor Java (cele din CLASSPATH). Tipul acestuia este ¸ java.lang.URLClassLoader. Intrucˆt tipurile de date Java pot fi ˆ arcate folosind diverse instante a ınc˘ ¸ de tip ClassLoader, fiecare obiect Class va retine class loader-ul care a ¸ fost folosit pentru ˆ arcare, acesta putˆnd fi obtinut cu metoda getClass- ınc˘ a ¸ Loader. Inc˘rcarea dinamic˘ a unei clase ˆ memorie se refer˘ la faptul c˘ nu a a ın a a cunoastem tipul acesteia decˆt la executia preogramului, moment ˆ care ¸ a ¸ ın putem solicita ˆ arcarea sa, specificˆnd numele s˘u complet prin intermediul ınc˘ a a unui ¸ir de caractere. Acest lucru poate fi realizat prin mai multe modalit˘¸i, s at cele mai comune metode fiind:
  • 449.
    448 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE • loadClass apelat˘ pentru un obiect de tip ClassLoader a ClassLoader loader = new MyClassLoader(); loader.loadClass("NumeCompletClasa"); • Class.forName Aceast˘ metoda va ˆ arca respectiva clas˘ folosind class loader-ul a ınc˘ a obiectului curent (care o apeleaz˘): a Class c = Class.forName("NumeCompletClasa"); // echivalent cu ClassLoader loader = this.getClass().getClassLoader(); loader.loadClass("ClasaNecunoscuta"); // Clasele standard pot fi si ele incarcate astfel Class t = Class.forName("java.lang.Thread"); Dac˘ dorim s˘ instantiem un obiect dintr-o clas˘ ˆ arcat˘ dinamic putem a a ¸ a ınc˘ a folosi metoda newInstance, cu conditia s˘ existe constructorul f˘r˘ argu- ¸ a aa mente pentru clasa respectiv˘. Dup˘ cum vom vedea ˆ sectiunea urm˘toare, a a ın ¸ a mai exist˘ ¸i alte posibilit˘¸i de a instantia astfel de obiecte. as at ¸ Class c = Class.forName("java.awt.Button"); Button b = (Button) c.newInstance(); Folosirea interfetelor sau a claselor abstracte ˆ ¸ ımpreun˘ cu ˆ arcarea di- a ınc˘ namic˘ a claselor ofer˘ un mecanism extrem de puternic de lucru ˆ Java. a a ın Vom detalia acest lucru prin intermediul unui exepmplu. S˘ presupunem a c˘ dorim s˘ cre˘m o aplicatie care s˘ genereze aleator un vector de numere a a a ¸ a dup˘ care s˘ aplice o anumit˘ functie acestui vector. Numele functiei care a a a ¸ ¸ trebuie apelat˘ va fi introdus de la tastatur˘, iar implementarea ei va fi a a continut˘ ˆ ¸ a ıntr-o clas˘ a directorului curent. Toate functiile vor extinde clasa a ¸ abstract˘ Functie. In felul acesta, aplicatia poate fi extins˘ cu noi functii a ¸ ¸ a ¸ f˘r˘ a schimba codul ei, tot ce trebuie s˘ facem fiind s˘ scriem noi clase care aa a a extind Functie ¸i s˘ implement˘m metoda executa. Aceasta va returna 0 s a a dac˘ metoda s-a executat corect, −1 ˆ caz contrar. a ın
  • 450.
    16.1. INCARCAREA CLASELORˆ MEMORIE ˘ IN 449 Listing 16.1: Exemplu de ˆ arcare dinamic˘ a claselor ınc˘ a import java . util .*; import java . io .*; public class TestFunctii { public static void main ( String args []) throws IOException { // Generam un vector aleator int n = 10; int v [] = new int [ n ]; Random rand = new Random () ; for ( int i =0; i < n ; i ++) v [ i ] = rand . nextInt (100) ; // Citim numele unei functii BufferedReader stdin = new BufferedReader ( new InputStreamReader ( System . in ) ) ; String numeFunctie = " " ; while (! numeFunctie . equals ( " gata " ) ) { System . out . print ( " nFunctie : " ) ; numeFunctie = stdin . readLine () ; try { // Incarcam clasa Class c = Class . forName ( numeFunctie ) ; // Cream un obiect de tip Functie Functie f = ( Functie ) c . newInstance () ; // Setam vectorul f . setVector ( v ) ; // sau f . v = v ; // Executam functia int ret = f . executa () ; System . out . println ( " nCod returnat : " + ret ) ; } catch ( Cl as sNo tF oun d E x c e p t i o n e ) { System . err . println ( " Functie inexistenta ! " ) ; } catch ( In st ant ia tio n E x c e p t i o n e ) { System . err . println ( " Functia nu poate fi instantiata ! "); } catch ( Il le gal Ac ces s E x c e p t i o n e ) { System . err . println ( " Functia nu poate fi accesata ! " ) ;
  • 451.
    450 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE } } } } Listing 16.2: Clasa abstract˘ ce descrie functia a ¸ public abstract class Functie { public int v [] = null ; public void setVector ( int [] v ) { this . v = v ; } public abstract int executa () ; } Listing 16.3: Un exemplu de functie ¸ import java . util .*; public class Sort extends Functie { public int executa () { if ( v == null ) return -1; Arrays . sort ( v ) ; for ( int i =0; i < v . length ; i ++) System . out . print ( v [ i ] + " " ) ; return 0; } } Listing 16.4: Alt exemplu de functie ¸ public class Max extends Functie { public int executa () { if ( v == null ) return -1; int max = v [0]; for ( int i =1; i < v . length ; i ++) if ( max < v [ i ]) max = v [ i ]; System . out . print ( max ) ; return 0;
  • 452.
    16.1. INCARCAREA CLASELORˆ MEMORIE ˘ IN 451 } } Un obiect de tip URLClassLoader mentine o list˘ de adrese URL de unde ¸ a va ˆıncerca s˘ ˆ a ıncarce ˆ memorie clasa al c˘rei nume ˆ specific˘m ca argu- ın a ıl a ment al metodelor de mai sus. Implicit, la crearea class loader-ului aceast˘ a list˘ este completat˘ cu informatiile din variabila sistem CLASSPATH sau cu a a ¸ cele specificate prin optiunea -classpath la lansarea aplicatiei. Folosind ¸ ¸ metoda getURLs putem afla aceste adrese, iar cu addURL putem ad˘uga a o nou˘ adres˘ de c˘utare a claselor. Bineˆ ¸eles, adresele URL pot specifica a a a ınt ¸i directoare ale sistemului de fi¸iere local. S˘ presupunem c˘ ˆ directorul s s a a ın c:clasedemo exist˘ clasa cu numele Test, aflat˘ ˆ pachetul demo ¸i dorim a a ın s s˘ o ˆ arc˘m dinamic ˆ memorie: a ınc˘ a ın // Obtinem class loaderul curent URLClassLoader urlLoader = (URLClassLoader) this.getClass().getClassLoader(); // Adaugam directorul sub forma unui URL urlLoader.addURL(new File("c:clase").toURL()); // Incarcam clasa urlLoader.loadClass("demo.Test"); Dup˘ ce o clas˘ a fost ˆ arcat˘ folosind un class loader, ea nu va mai a a ınc˘ a putea fi desc˘rcat˘ explicit din memorie. In cazul ˆ care dorim s˘ avem posi- a a ın a bilitatea de a o reˆ arca, deoarece a fost modificat˘ ¸i recompilat˘, trebuie s˘ ınc˘ as a a folosim class-loadere proprii ¸i s˘ instantiem noi obiecte de tip ClassLoader, s a ¸ ori de cˆte ori dorim s˘ fort˘m reˆ arcarea claselor. Crearea unui class a a ¸a ınc˘ loader propriu se face uzual prin extinderea clasei URLClassLoader, o vari- ant˘ simplist˘ fiind prezentat˘ mai jos: a a a public class MyClassLoader extends URLClassLoader{ public MyClassLoader(URL[] urls){ super(urls); } } Inc˘rcarea claselor folosind clasa nou creat˘ se va face astfel: a a
  • 453.
    452 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE // La initializare URLClassLoader systemLoader = (URLClassLoader) this.getClass().getClassLoader(); URL[] urls = systemLoader.getURLs(); // Cream class loaderul propriu MyClassLoader myLoader = new MyClassLoader(urls); myLoader.loadClass("Clasa"); ... // Dorim sa reincarcam clasa myLoader.loadClass("Clasa"); // nu functioneaza ! // Cream alt class loader MyClassLoader myLoader = new MyClassLoader(urls); myLoader.loadClass("Clasa"); // reincarca clasa 16.2 Mecanismul reflect˘rii a Mecanismul prin care o clas˘, interfat˘ sau obiect ”reflect˘” la momentul a ¸a a executiei structura lor intern˘ se nume¸te reflectare (eng. reflection), acesta ¸ a s punˆnd la dispozitie metode pentru: a ¸ • Determinarea clasei unui obiect. • Aflarea unor informatii despre o clas˘ (modificatori, superclasa, con- ¸ a structori, metode). • Instantierea unor clase al c˘ror nume este ¸tiut abia la executie. ¸ a s ¸ • Setarea sau aflarea atributelor unui obiect, chiar dac˘ numele acestora a este ¸tiut abia la executie. s ¸ • Invocarea metodelor unui obiect al c˘ror nume este ¸tiut abia la executie. a s ¸ • Crearea unor vectori a c˘ror dimensiune ¸i tip nu este ¸tiut decˆt la a s s a executie. ¸ Suportul pentru reflectare este inclus ˆ distributia standard Java, fiind ın ¸ cunoscut sub numele de Reflection API ¸i contine urm˘toarele clase: s ¸ a
  • 454.
    ˘ 16.2. MECANISMUL REFLECTARII 453 • java.lang.Class • java.lang.Object • Clasele din pachetul java.lang.reflect ¸i anume: s – Array – Constructor – Field – Method – Modifier 16.2.1 Examinarea claselor ¸i interfetelor s ¸ Examinarea claselor ¸i interfetelor se realizeaz˘ cu metode ale clasei java.lang.Class, s ¸ a un obiect de acest tip putˆnd s˘ reprezinte atˆt o clas˘ cˆt ¸i o interfat˘, a a a a a s ¸a diferentierea acestora f˘cˆndu-se prin intermediul metodei isInterface. ¸ a a Reflection API pune la dispozitie metode pentru obtinerea urm˘toarelor ¸ ¸ a informatii: ¸ Aflarea instantei Class corespunz˘tor unui anumit obiect sau tip de ¸ a date: Class c = obiect.getClass(); Class c = java.awt.Button.class; Class c = Class.forName("NumeClasa"); Tipurile primitive sunt descrise ¸i ele de instante de tip Class avˆnd forma s ¸ a TipPrimitiv.class: int.class, double.class, etc., diferentierea lor f˘cˆndu- ¸ a a se cu ajutorul metodei isPrimitive. Aflarea numelui unei clase - Se realizeaz˘ cu metoda getName: a Class clasa = obiect.getClass(); String nume = clasa.getName();
  • 455.
    454 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE Aflarea modificatorilor unei clase - Se realizeaz˘ cu metoda getModifiers, a aceasta returnˆnd un num˘r ˆ a a ıntreg ce codific˘ toti modificatorii clasei. Pen- a ¸ tru a determina u¸or prezenta unui anumit modificator se folosesc metodele s ¸ statice ale clasei Modifier isPublic, isAbstract ¸i isFinal: s Class clasa = obiect.getClass(); int m = clasa.getModifiers(); String modif = ""; if (Modifier.isPublic(m)) modif += "public "; if (Modifier.isAbstract(m)) modif += "abstract "; if (Modifier.isFinal(m)) modif += "final "; System.out.println(modif + "class" + c.getName()); Aflarea superclasei - Se realizeaz˘ cu metoda getSuperclass ce re- a turneaz˘ o instant˘ de tip Class, corespunz˘toare tipului de date al super- a ¸a a clasei sau null pentru clasa Object. Class c = java.awt.Frame.class; Class s = c.getSuperclass(); System.out.println(s); // java.awt.Window Class c = java.awt.Object.class; Class s = c.getSuperclass(); // null Aflarea interfetelor implementate de o clas˘ sau extinse de o interfat˘ ¸ a ¸a - Se realizeaz˘ cu metoda getInterfaces, ce returneaz˘ un vector de tip a a Class[]. public void interfete(Class c) { Class[] interf = c.getInterfaces(); for (int i = 0; i < interf.length; i++) { String nume = interf[i].getName(); System.out.print(nume + " ");
  • 456.
    ˘ 16.2. MECANISMUL REFLECTARII 455 } } ... interfete(java.util.HashSet.class); // Va afisa interfetele implementate de HashSet: // Cloneable, Collection, Serializable, Set interfete(java.util.Set); // Va afisa interfetele extinse de Set: // Collection Aflarea variabilelor membre - Se realizeaz˘ cu una din metodele a getFields sau getDeclaredFields, ce returnez˘ un vector de tip Field[], a diferenta ˆ ¸ ıntre cele dou˘ constˆnd ˆ faptul c˘ prima returneaz˘ toate vari- a a ın a a abilele membre, inclusiv cele mo¸tenite, ˆ timp ce a doua le returnez˘ doar s ın a pe cele declarate ˆ cadrul clasei. La rˆndul ei, clasa Field pune la dispozitie ın a ¸ metodele getName, getType ¸i getModifiers pentru a obtine numele, tipul, s ¸ respectiv modificatorii unei variabile membru. Cu ajutorul metodei getField este posibil˘ obtinerea unei referinte la o a ¸ ¸ variabil˘ mebr˘ cu un anumit nume specificat. a a Aflarea constructorilor - Se realizeaz˘ cu metodele getConstructors a sau getDeclaredConstructors, ce returneaz˘ un vector de tip Constructor[]. a Clasa Constructor pune la dispozitie metodele getName, getParameterTypes, ¸ getModifiers, getExceptionTypes pentru a obtine toate informatiile legate ¸ ¸ de respectivul constructor. Cu ajutorul metodei getConstructor este posibil˘ obtinerea unei referinte a ¸ ¸ la constructor cu o signatur˘ specificat˘. a a Aflarea metodelor - Se realizeaz˘ cu metodele getMethods sau a getDeclaredMethods, ce returneaz˘ un vector de tip Method[]. Clasa Method a pune la dispozitie metodele getName, getParameterTypes, getModifiers, ¸ getExceptionTypes, getReturnType pentru a obtine toate informatiile legate ¸ ¸
  • 457.
    456 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE de respectiva metod˘. a Cu ajutorul metodei getMethod este posibil˘ obtinerea unei referinte la a ¸ ¸ o metod˘ cu o signatur˘ specificat˘. a a a Aflarea claselor imbricate - Se realizeaz˘ cu metodele getClasses sau a getDeclaredClasses, ce returnez˘ un vector de tip Class[]. a Aflarea clasei de acoperire - Se realizeaz˘ cu metoda getDeclaringClass. a Aceast˘ metod˘ o reg˘sim ¸i ˆ clasele Field, Constructor, Method, pentru a a a s ın acestea returnˆnd clasa c˘rei ˆ apartine variabila, constructorul sau metoda a a ıi ¸ respectiv˘. a 16.2.2 Manipularea obiectelor Pe lˆng˘ posibilitatea de a examina structura unei anumite clase la momentul a a executiei, folosind Reflection API avem posibilitatea de a lucra dinamic cu ¸ obiecte, bazˆndu-ne pe informatii pe care le obtinem abia la executie. a ¸ ¸ ¸ Crearea obiectelor Dup˘ cum stim, crearea obiectelor se realizeaz˘ cu operatorul new urmat de a ¸ a un apel la un constructor al clasei pe care o instantiem. In cazul ˆ care ¸ ın numele clasei nu este cunoscut decˆt la momentul executiei nu mai putem a ¸ folosi aceast˘ metod˘ de instantiere. In schimb, avem la dispozitie alte dou˘ a a ¸ ¸ a variante: • Metoda newInstance din clasa java.lang.Class Aceasta permite instantierea unui obiect folosind constructorul f˘r˘ ar- ¸ aa gumente al acestuia. Dac˘ nu exist˘ un astfel de constructor sau nu este a a accesibil vor fi generate exceptii de tipul InstantiationException, re- ¸ spectiv IllegalAccessException. Class c = Class.forName("NumeClasa"); Object o = c.newInstance(); // Daca stim tipul obiectului
  • 458.
    ˘ 16.2. MECANISMUL REFLECTARII 457 Class c = java.awt.Point.class; Point p = (Point) c.newInstance(); • Metoda newInstance din clasa Constructor Aceasta permite instantierea unui obiect folosind un anumit construc- ¸ tor, cel pentru care se face apelul. Evident, aceast˘ solutie presupune a ¸ ˆ primul rˆnd obtinerea unui obiect de tip Constructor cu o anumit˘ ın a ¸ a signatur˘, apoi specificarea argumentelor la apelarea sa. S˘ rescriem a a exemplul de mai sus, apelˆnd constructorul cu dou˘ argumente al clasei a a Point. Class clasa = java.awt.Point.class; // Obtinem constructorul dorit Class[] signatura = new Class[] {int.class, int.class}; Constructor ctor = clasa.getConstructor(signatura); // Pregatim argumentele // Ele trebuie sa fie de tipul referinta corespunzator Integer x = new Integer(10); Integer y = new Integer(20); Object[] arg = new Object[] {x, y}; // Instantiem Point p = (Point) ctor.newInstance(arg); Exceptii generate de metoda newInstance sunt: InstantiationException, ¸ IllegalAccessException, IllegalArgumentException ¸i s InvocationTargetException. Metoda getConstructor poate provoca exceptii ¸ de tipul NoSuchMethodException. Invocarea metodelor Invocarea unei metode al c˘rei nume ˆ cunoa¸tem abia la momentul executiei a ıl s ¸ se realizeaz˘ cu metoda invoke a clasei Method. Ca ¸i ˆ cazul construc- a s ın torilor, trebuie s˘ obtinem ˆ ai o referint˘ la metoda cu signatura core- a ¸ ıntˆ ¸a spunz˘toare ¸i apoi s˘ specific˘m argumentele. In plus, mai putem obtine a s a a ¸ valoarea returnat˘. S˘ presupunem c˘ dorim s˘ apel˘m metoda contains a a a a a
  • 459.
    458 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE a clasei Rectangle care determin˘ dac˘ un anumit punct se g˘se¸te ˆ inte- a a a s ın riorul drepunghiului. Metoda contains are mai multe variante, noi o vom apela pe cea care accept˘ un argument de tip Point. a Class clasa = java.awt.Rectangle.class; Rectangle obiect = new Rectangle(0, 0, 100, 100); // Obtinem metoda dorita Class[] signatura = new Class[] {Point.class}; Method metoda = clasa.getMethod("contains", signatura); // Pregatim argumentele Point p = new Point(10, 20); Object[] arg = new Object[] {p}; // Apelam metoda metoda.invoke(obiect, arg); Dac˘ num˘rul argumentelor metodei este 0, atunci putem folosi val- a a oarea null ˆ locul vectorilor ce reprezint˘ signatura, respectiv parametri ın a de apelare ai metodei. Exceptiile generate de metoda invoke sunt: IllegalAccessException ¸ ¸i InvocationTargetException. Metoda getMethod poate provoca exceptii s ¸ de tipul NoSuchMethodException. Setarea ¸i aflarea variabilelor membre s Pentur setarea ¸i aflarea valorilor variabilelor membre sunt folosite metodele s set ¸i get ale clasei Field. S˘ presupunem c˘ dorim s˘ set˘m variabila x a s a a a a unui obiect de tip Point ¸i s˘ obtinem valoarea variabilei y a aceluia¸i obiect: s a ¸ s Class clasa = java.awt.Point.class; Point obiect = new Point(0, 20); // Obtinem variabilele membre Field x, y; x = clasa.getField("x"); y = clasa.getField("y");
  • 460.
    ˘ 16.2. MECANISMUL REFLECTARII 459 // Setam valoarea lui x x.set(obiect, new Integer(10)); // Obtinem valoarea lui y Integer val = y.get(obiect); Exceptiile generate de metodele get, set sunt: IllegalAccessException ¸ ¸i IllegalArgumentException. Metoda getField poate provoca exceptii de s ¸ tipul NoSuchFieldException. Revenind la exemplul din sectiunea anterioar˘ cu apelarea dinamic˘ a ¸ a a unor functii pentru un vector, s˘ presupunem c˘ exist˘ deja un num˘r ˆ ¸ a a a a ınsemnat de clase care descriu diferite functii dar acestea nu extind clasa abstract˘ ¸ a Functie. Din acest motiv, solutia anterioar˘ nu mai este viabil˘ ¸i trebuie ¸ a as s˘ folosim apelarea metodei executa ˆ a ıntr-un mod dinamic. Listing 16.5: Lucru dinamic cu metode ¸i variabile s import java . lang . reflect .*; import java . util .*; import java . io .*; public class TestFunctii2 { public static void main ( String args []) throws IOException { // Generam un vector aleator int n = 10; int v [] = new int [ n ]; Random rand = new Random () ; for ( int i =0; i < n ; i ++) v [ i ] = rand . nextInt (100) ; // Citim numele unei functii BufferedReader stdin = new BufferedReader ( new InputStreamReader ( System . in ) ) ; String numeFunctie = " " ; while (! numeFunctie . equals ( " gata " ) ) { System . out . print ( " nFunctie : " ) ; numeFunctie = stdin . readLine () ;
  • 461.
    460 CAPITOLUL 16. LUCRUL DINAMIC CU CLASE try { // Incarcam clasa Class c = Class . forName ( numeFunctie ) ; // Cream un obiect de tip Functie Object f = c . newInstance () ; // Setam vectorul ( setam direct variabila v ) Field vector = c . getField ( " v " ) ; vector . set (f , v ) ; // Apelam metoda ’ executa ’ // Folosim null pentru ca nu avem argumente Method m = c . getMethod ( " executa " , null ) ; Integer ret = ( Integer ) m . invoke (f , null ) ; System . out . println ( " nCod returnat : " + ret ) ; } catch ( Exception e ) { System . err . println ( " Eroare la apelarea functiei ! " ) ; } } } } 16.2.3 Lucrul dinamic cu vectori Vectorii sunt reprezentati ca tip de date tot prin intermediul clasei java.lang.Class, ¸ diferentierea f˘cˆndu-se prin intermediul metodei isArray. ¸ a a Tipul de date al elementelor din care este format vectorul va fi obtinut cu ¸ ajutorul metodei getComponentType, ce ˆ ıntoarce o referint˘ de tip Class. ¸a Point []vector = new Point[10]; Class c = vector.getClass(); System.out.println(c.getComponentType()); // Va afisa: class java.awt.Point Lucrul dinamic cu obiecte ce reprezint˘ vectori se realizeaz˘ prin inter- a a mediul clasei Array. Aceasta contine o serie de metode statice ce permit: ¸ • Crearea de noi vectori: newInstance • Aflarea num˘rului de elemente: getLength a
  • 462.
    ˘ 16.2. MECANISMUL REFLECTARII 461 • Setarea / aflarea elementelor: set, get Exemplul de mai jos creeaz˘ un vector ce contine numerele ˆ a ¸ ıntregi de la 0 la 9: Object a = Array.newInstance(int.class, 10); for (int i=0; i < Array.getLength(a); i++) Array.set(a, i, new Integer(i)); for (int i=0; i < Array.getLength(a); i++) System.out.print(Array.get(a, i) + " ");