Menu


Trascrizione diapositive

1. Identificatori, tipi di dati ed array

  • Complessità: BASSA.


2. Stile di codifica

  • Prima di effettuare una carrellata sulle regole per quanto riguarda gli identificatori, è giusto definire lo stile di codifica che usa il compilatore javac per interpretare il codice sorgente scritto in Java, possiamo affermare quindi che Java è:
    • A schema libero.
      Case sensitive.
      Supporta i commenti.
      Ha parole chiave.
      Possiede regole ferree per i tipi di dati ed alcune convenzioni per i nomi.


    3. Schema libero

    • Possiamo scrivere un intero programma Java tutto su di un’unica riga di codice, oppure andando a capo ad ogni parola scritta: il compilatore compilerà ugualmente il codice se esso è corretto, chiaramente lo sviluppatore potrebbe avere seri problemi a capire il significato del codice da lui scritto!
    • Per questo esistono diversi stili di formattazione del codice che permettono di evitare all’inesperto programmatore come anche al professionista di commettere semplici errori o dimenticanze: come la chiusura di una parentesi graffa in una classe o in un metodo, o di dimenticare il «;» (errore per altro molto comune), oppure di dimenticare le parentesi tonde durante l’invocazione di un metodo.


    4. Schema di formattazione corretto

    • Per questo è consigliato al lettore di abituarsi a scrivere ogni istruzione in modo corretto procedendo nel seguente modo:

    • Passo 1 - dichiarazione:
      class Classe { }


      Passo 2 - formattazione:
      class Classe {

      }


      Passo 3 - completamento:
      class Classe {

        public int intero;
        // Segue il resto del codice
      }


    5. Commenti

    • Commentare in modo corretto il codice è il modo migliore per poter effettuare future modifiche ad esso in modo semplice e veloce.
    • Java supporta tre tipi di commenti al codice:

    • Commento su un’unica riga:

      // Commento su una sola riga di codice

      Commento su più righe:

      /*
           Questo è un commento
           su più righe
      */


      Commento Javadoc:

      /**
           Questo è un commento
           Javadoc
      */


    • Il commento Javadoc permette di produrre un file di documentazione del codice in formato HTML.


    6. Come generare documentazione Javadoc

    7. Regole per gli identificatori

    • Gli identificatori sono i nomi che vengono assegnati a metodi, classi, oggetti, variabilicostanti, interfacce, enumerazioni, annotazioni.
    • Un identificatore non può coincidere con una parola chiave di Java. Una parola chiave ha un certo significato per il compilatore, quindi se usata per un identificatore produrrà inevitabilmente un messaggio di errore o (ancora peggio) riusciremo a compilare il file, ma in fase di esecuzione il programma genererà risultati inaspettati.


    8. Elenco completo delle parole chiave di Java

      [Tabella] Elenco delle parole chiave

      Ribadiamo una volta per tutte che non possiamo ad esempio chiamare una variabile private, oppure un metodo new.


    9. Regole per gli identificatori

    • Stabilito che un identificatore non può coincidere con una parola chiave, bisogna tenere conto di altre due regole importanti.
      Il primo carattere può essere A-Z, a-z, _, $.
      Il secondo ed i successivi caratteri possono essere A-Z, a-z, _, $, 0-9.
    • Quindi «5s» non è un identificatore valido, mentre «s5» è invece un identificatore valido.


    10. Regole facoltative per gli identificatori

    • Gli identificatori devono essere significativi, se scriviamo un programma utilizzando la classe «a», che definisce al suo interno la variabile «b», «c» e il metodo «d», sicuramente ridurremo la comprensibilità del programma stesso.
    • Un identificatore di classe (ma questo vale anche per le interfacce, enumerazioni ed annotazioni) deve sempre iniziare per lettera maiuscola e se composto da più parole (considerando che il compilatore non può intuire gli spazi) esse non si possono separare, quindi bisogna usare il maiuscolo nella prima lettera dopo un immaginario spazio, come ad esempio: «MacchinaDaCorsa», «FiguraGeometrica».
    • Di solito l’identificatore di una variabile è composto da uno o più sostantivi, ad esempio «larghezza», «altezza» o «numeroLati». L’identificatore di una variabile deve cominciare sempre per lettera minuscola e in caso sia composto da più parole (considerando che il compilatore non può intuire gli spazi) esse non si possono separare, quindi bisogna usare il maiuscolo nella prima lettera dopo un immaginario spazio, come ad esempio «numeroLati».
    • Di solito l’identificatore di un metodo conterrà verbi, ad esempio: «stampaScontrino()», «somma()», «sottrazione()». Come il lettore certamente noterà per l’identificatore di un metodo valgono le stesse regole che hanno riguardato l’identificatore di una variabile, senonché l’identificatore di un metodo è sempre seguito dalle parentesi tonde.
    • L’identificatore di una costante invece deve avere tutte le lettere maiuscole e se composto da più parole, queste si separano con un underscore (simbolo trattino-basso). Ad esempio:

    • «PI_GRECO»
      «NUMERO_RUOTE»


    11. Tipi di dato

  • Java definisce otto tipi di dati primitivi:
    • Tipi interi: byte, short, int, long.
      Tipi con virgola mobile: float e double.
      Tipo carattere: char.
      Tipo logico-booleano: boolean.
    In Java non esistono tipi di dato senza segno (unsigned) e per rappresentare numeri interi negativi viene utilizzata la regola del completamento a due.


    12. Tipi di dati interi

    • I tipi di dati interi sono quattro: byte, short, int, long. Essi condividono tutti la stessa funzionalità, e cioè quella di immagazzinare numeri interi positivi o negativi, ma differiscono per quanto riguarda il loro intervallo di rappresentazione:
    [Tabella] Tipi di dati interi
    Si noti che vengono rappresentati in numero pari i tipi numerici positivi ed i tipi numerici negativi, in quanto il numero «0» è considerato numero positivo.


    13. Considerazioni su i numeri

    • Quando si tratta con tipi di dati che rappresentano numeri, bisogna stare sempre molto attenti all’intervallo di rappresentazione, in quanto se è vero che l’istruzione:

      int i = 2147483648;
    provoca un errore in compilazione, perché il compilatore si accorge che il numero che si tenta di assegnare alla variabile «i» è fuori dal range di rappresentazione, è anche vero che le seguenti istruzioni:

    int a = 2147483647; //Massimo valore per un int
    int b = 1;
    int risultato = a+b;

    non restituiranno nessun errore in compilazione ma il valore della variabile «risultato» sarà «-2147483648»! E’ evidente che si tratta di un risultato errato (di overflow) ed inaspettato per il compilatore, ma non deve esserlo per noi!


    14. Promotion

    • Il meccanismo della promotion si attiva quando dobbiamo operare con variabili anche di diversa tipologia, consideriamo la seguente istruzione:

      byte b = 50;
    L’istruzione viene compilata correttamente in quanto il compilatore riconosce il numero «50» come un intero, ma si accorge che è possibile convertirlo in byte, in quanto il numero è tranquillamente compreso nell’intervallo che va da -128 a +127.
    • Adesso consideriamo queste due istruzioni in sequenza:

      byte b = 50;
      b = b*2;
    Per la prima istruzione vale il discorso fatto in precedenza, ma la seconda istruzione genererà un errore, in quanto il compilatore non effettuerà prima l’operazione di moltiplicazione, per poi controllare la compatibilità con il tipo di dato dichiarato; ma analizzerà il primo operando «b» e capirà che si tratta di una variabile di tipo byte, poi analizzerà anche il secondo operando «2» e capirà che si tratta semplicemente di un intero (int), quindi convertirà i due operandi in int (promotion), una volta che i due operandi sono di tipo intero, vengono formulate delle ipotesi su i possibili valori che può assumere la variabile a questo punto intera «b», se ad esempio la variabile «b» assumesse il valore «127» è chiaro che il risultato dell’operazione «254» non potrà essere immagazzinato in una variabile di tipo byte, proprio per questo ci viene segnalato errore! Il compilatore ha l’esigenza di immagazzinare il risultato in una variabile di tipo int, per ridurre al minimo la possibilità di un eventuale errore di calcolo.


    15. Promotion

    • C’è da precisare rispetto a quello che abbiamo appena affermato, che quando si tratta di fare operazioni con variabili ed una di queste è di tipo byte, short o int e se non è presente nessun operando di tipo long, float o double, viene eseguita la conversione di tutti gli operandi in int, quindi il compilatore nell’esempio precedente non ha convertito in int perché il numero «2» era dichiarato implicitamente come numero intero, ma per semplice convenzione.
    • La stessa convenzione prevede che, se è presente un operando di tipo long (e non è presente un operando di tipo float o double), tutti gli altri operandi verranno convertiti in long, e chiaramente il risultato dovrà essere immagazzinato in una variabile di tipo long.
    • Inoltre la convenzione prevede che, se è presente un operando di tipo float (e non è presente un operando di tipo double), tutti gli altri operandi verranno convertiti in float, e chiaramente il risultato dovrà essere immagazzinato in una variabile di tipo float.
    • Infine la convenzione prevede che, se è presente un operando di tipo double, tutti gli altri operandi verranno convertiti in double, e chiaramente il risultato dovrà essere immagazzinato in una variabile di tipo double.


    16. Casting

    • È anche possibile forzare l’immagazzinamento di un risultato in un certo tipo di dato, grazie ad una tecnica nota come casting. Questa tecnica bisogna usarla consapevolmente, infatti, se il numero del risultato rappresentato ad esempio da un intero (int), si trova fuori dall’intervallo di rappresentazione in questo caso di un byte o di uno short, incorreremo al momento dell’assegnazione forzata del risultato alla variabile in una perdita di precisione. Quindi il casting deve essere effettuato se e solo se si è certi di non incorrere in perdita di precisione!
    • Segue un esempio corretto di casting:

      byte b = 50;
      b = (byte) (b*2);


      Sappiamo bene che il risultato restituito (b*2=100) sotto forma di numero intero rientra anche nell’intervallo di rappresentazione di una variabile di tipo byte, quindi in questo caso forzare l’immagazzinamento di un risultato di tipo int, in una variabile di tipo byte, non causerà nessun problema, il compilatore quindi sarà avvertito che un eventuale perdita di precisione (che  in questo caso non c’è) è calcolata e sotto controllo.
    • Mentre se scrivessimo:

      b = (byte) 128; //Il massimo per un byte è 127
    Il compilatore non segnalerebbe alcun messaggio di errore, ma a causa del troncamento dei 24 bit in eccedenza si otterrà un risultato errato, quindi incorreremo inevitabilmente in una perdita di precisione.


    17. Casting

    • Se impieghiamo una variabile di tipo long è giusto porre l’accento sul fatto che; a meno di effettuare un casting esplicito, essa sarà sempre inizializzata con un valore intero, quindi se scriviamo:

      long l = 3000;

      Bisogna tener presente che il valore «3000» è un intero per default, ma il compilatore non ci segnalerà nessun tipo di errore perché un int può essere tranquillamente immagazzinato in un long.
    • Ma è sempre buona norma tipica del bravo programmatore, scrivere in questo modo:

    • long l = 3000L;
    In questo modo il valore «3000» è un long.

    Nel caso inizializzassimo una variabile di tipo long a «3000000000», quindi ad un valore che non fa parte dell’intervallo di rappresentazione di un int, incorreremo in un errore in compilazione.


    18. Rappresentazione di un numero intero

    • Per immagazzinare un intero si possono usare tre forme:
    • Decimale:

      int i = 1000;

      Ottale:

      short s = 022;

      Esadecimale:

      long l = 0x12acd;

    • Come possiamo osservare per la notazione ottale basta anteporre al numero uno «0» (zero), mentre per la notazione esadecimale basta anteporre uno «0» (zero) seguito da una «x» (indifferentemente: maiuscola o minuscola).
    Anche un long può essere rappresentato in formato decimale, ottale o esadecimale, bisogna però avere l’accortezza di aggiungere  il carattere L alla fine del valore espresso, come abbiamo visto nella precedente slide.


    19. Tipi di dati in virgola mobile

    • Java utilizza per i valori in virgola mobile lo standard di decodifica IEEE 754-1985. Qui di seguito i due tipi di dato in floating point che possiamo utilizzare, con il loro relativo intervallo di rappresentazione:

      [Tabella] Tipi di dati in virgola mobile
      E’ possibile utilizzare la notazione esponenziale o ingegneristica.


    20. Considerazione sui numeri

    • Siamo in presenza di overflow quando il risultato di un operazione diventa troppo grande per essere rappresentato in una variabile.
    • Siamo in presenza di underflow quando il risultato di un operazione diventa troppo piccolo per essere rappresentato in una variabile.
    • In un operazione aritmetica tra numeri interi si potrebbe generare un overflow (come abbiamo visto in precedenza), esso non viene gestito dal compilatore (nel senso che viene memorizzato un valore errato nella variabile che dovrebbe contenere il risultato).
    • L’aritmetica in virgola mobile può dar luogo invece ad un overflow o ad un underflow, ma a differenza di una operazione fatta tra numeri interi, l’overflow viene gestito meglio dal compilatore, nel senso che viene immagazzinato nella variabile che contiene il risultato un valore verso infinito, mentre in caso di underflow viene immagazzinato un valore arrotondato a «0».


    21. Particolarità dei numeri in virgola mobile

    • Nel caso in cui il risultato di un espressione in virgola mobile non sia valido (come ad esempio la divisione di un infinito per se stesso) verrà generato un valore «NaN», che sta per «Not a Number» cioè «Non è un Numero».
    • Considerando che le operazioni aritmetiche in virgola mobile possono restituire risultati che non sono compresi in numeri reali, è chiaro che queste variabili possono anche essere inizializzate a valori non appartenenti all’insieme dei numeri reali, vediamo come:

    • Variabile di tipo float inizializzata a valore «NaN»

      float f = Float.NaN;

      Variabile di tipo float inizializzata a valore «infinito negativo»

      float f = Float.NEGATIVE_INFINITY;

      Variabile di tipo float inizializzata a valore «infinito positivo»

      float f = Float.POSITIVE_INFINITY;

      Variabile di tipo double inizializzata a valore «NaN»

      double d = Double.NaN;

      Variabile di tipo double inizializzata a valore «infinito negativo»

      double d = Double.NEGATIVE_INFINITY;

      Variabile di tipo double inizializzata a valore «infinito positivo»

      double d = Double.POSITIVE_INFINITY;


    22. Casting in virgola mobile

    • Se adoperiamo una variabile di tipo float è giusto porre l’accento sul fatto che a meno di effettuare un casting esplicito, essa sarà sempre inizializzata con un valore double, quindi se scriviamo:

      float f = 3.14;
    • Incapperemo sicuramente in un errore in compilazione, in quanto un double non può essere contenuto in un float, anche in questo caso il linguaggio ci viene incontro permettendoci di effettuare il casting con una sintassi breve:

      float f = 3.14F;
    In questo caso il valore «3.14» è un float.


    23. Tipo carattere

    • Il tipo carattere char permette di acquisire per poi eventualmente visualizzare un carattere singolo, codificandolo e decodificandolo tramite unicode.
    • Unicode è un sistema di codifica che assegna un numero univoco ad ogni carattere, in maniera indipendente dalla lingua, dalla piattaforma informatica e dal programma utilizzato.
    • Possiamo assegnare ad un char un qualsiasi carattere presente nella nostra tastiera o direttamente il numero (espresso in esadecimale) assegnato ad ogni carattere da unicode, ricordandosi però che in quest’ultimo caso dobbiamo anteporre alla dichiarazione del valore il prefisso «\u». Di seguito vengono mostrati esempi di acquisizione di singolo carattere attraverso Java:

      char lettera = 'a';
      char chiocciola = '@';
      char dollaro = '\u0024';


    • Per visualizzare una variabile di tipo char basta utilizzare la classica sintassi «System.out.println()»,passando come parametro il nome della variabile (o il riferimento ad essa) ad esempio in questo modo:

      System.out.println(lettera);
      System.out.println(chiocciola);
      System.out.println(dollaro);


    24. Caratteri speciali per il tipo char

    • Esistono alcuni caratteri speciali che vengono spesso immagazzinati in un char durante la programmazione professionale:

      [Tabella] Tipo carattere
      Per maggiori informazioni su unicode consultare:
      http://www.unicode.org


    25. Tipo logico-booleano

    • Il tipo di dato boolean utilizza un solo bit, quanto basta per memorizzare lo stato logico «1» (true) o «0» (false). Esempio:

      boolean b = true;

    • oppure

      boolean b = false;
    • Chiaramente lo stato di un boolean può cambiare durante l’esecuzione del programma, cioè passare da «false» a «true» o da «true» a «false».


    26. Tipi di dati non primitivi

    • Quando si parla di un tipo di dato non primitivo, si intende il processo mediante il quale si dichiara un oggetto, di seguito la classica sintassi:

      nomeClasse nomeOggetto;

    • Il «nomeOggetto» meglio riconosciuto dai programmatori Java come «reference» non è il nome di una semplice variabile, ma di un tipo particolare di variabile detta puntatore, essa sarà predisposta per accettare al suo interno un indirizzo di memoria generato dalla sua istanza.


    27. Tipi di dati non primitivi

    Di seguito un esempio pratico che ci mostra la differenza tra una varabile primitiva ed una variabile non primitiva:

    class Mercato {

                            public int codiceNazione;
                            public int codiceProdotto;
                            public double prezzo;

    }

    public class Programma {

      public static void main(String args[]) {
                            int unNumero = 5;
                            int unAltroNumero = unNumero;
                            Mercato unMercato = new Mercato();
                            Mercato unAltroMercato = unMercato;
      }

    }

    Allocazione in memoria
    Come possiamo notare la variabile primitiva «unNumero» è completamente indipendente dalla variabile primitiva «unAltroNumero», mentre il reference «unMercato» punta alla stessa area di memoria di «unAltroMercato».


    28. Tipi di dati non primitivi

    • Nel caso precedente, quindi, una chiamata del tipo:

      unMercato.codiceNazione = 0;
    è assolutamente equivalente alla chiamata:
      unAltroMercato.codiceNazione = 0;
    questo perché i due reference puntano allo stesso oggetto e quindi alla stessa variabile.
    • Mentre se avessimo voluto cambiare il valore della variabile «unNumero» avremmo dovuto procedere in questo modo:

      unNumero = 0;
    se avessimo scritto in quest’altro modo:
      unAltroNumero = 0;
    sarebbe cambiato il valore della variabile «unAltroNumero», senza alterare il valore della variabile «unNumero» (come già asserito nella precedente slide).

    29. Il tipo String

    • In Java le stringhe vengono dichiarate con il suffisso String, una variabile di tipo String ha però la particolarità di essere un tipo di dato non primitivo, pertanto ogni stringa dovrebbe essere istanziata con una sintassi simile alla seguente:

      String nome = new String("Mario Rossi");

    • A questo punto è lecito domandarsi dove si trova la classe «String», che predisporrà al suo interno un costruttore che accetterà il parametro «Mario Rossi» e che sarà in grado di processarlo correttamente. È bene sapere che in ogni file Java è importato automaticamente un cosiddetto package chiamato «java.lang», senza il quale non avremmo potuto dichiarare ed istanziare nessun oggetto di tipo stringa.


    30. Semplificazione per il programmatore

    • Il tipo stringa gode di una particolarità interessante che lo contraddistingue da tutti gli altri tipi di dato non primitivo, questa particolarità semplifica e velocizza la stesura del codice da parte del programmatore. Come se si trattasse di un tipo di dato primitivo, una stringa può essere dichiarata ed istanziata anche in questo modo:

      String nome = "Mario Rossi";

      che è quindi equivalente a:

      String nome = new String("Mario Rossi");


    31. Metodi della classe String

    • La classe «String» permette di utilizzare una serie di metodi, come ad esempio: «toUpperCase()», «toLowerCase()» o «equals(String)».
    • Ecco una tabella che per il momento dovrebbe risultare sufficientemente esaustiva:

      [Tabella] Metodi della classe String


    32. Array in Java

    • Gli array possono considerarsi come una semplice collezione di dati primitivi, di dati non primitivi o di altri array.

      Array in Java

      Nell’immagine a fianco è mostrato in modo molto accurato e preciso, come viene allocato in memoria un array. Possiamo subito notare che all’atto della creazione del vettore viene specificata la dimensione che in questo caso è pari a «6», ad ogni elemento presente nell’array viene quindi contrassegnato un indice progressivo che parte sempre da «0», nel caso mostratoci notiamo che per realizzare «6» elementi: l’indice parte (come già affermato) da «0» per poi arrivare fino a «5». All’interno di ogni elemento contrassegnato è presente il dato vero e proprio.

    • Per realizzare un array in Java bisogna passare attraverso tre fasi: dichiarazione, creazione ed inizializzazione.


    33. Dichiarare un array in Java

    • Nel processo di dichiarazione di un array bisogna specificare il tipo di dato che dovrà essere immagazzinato all’interno della collezione, segue un esempio di dichiarazione di un array di char:

      char alfabeto [];

    un altro tipo di dichiarazione di array assolutamente equivalente è questo:
      char [] alfabeto;
    • Al programmatore la scelta di quale sia la forma più pratica e conveniente per dichiarare un array.


    34. Creazione di un array in Java

    • Nel processo di creazione di un array, bisogna specificare la dimensione, cioè il numero reale di elementi che dovranno far parte della collezione. Di seguito un esempio pratico di come realizzare un array di 21 elementi di tipo char:

      alfabeto = new char[21];

    • Ricordiamo che senza aver dichiarato l’array prima, non possiamo poi crearlo.
    • Dopo la creazione vera e propria del vettore: il contenuto di ogni elemento è inizializzato automaticamente a valore nullo.


    35. Inizializzazione di un array in Java

    • Il processo di inizializzazione permette di sostituire il valore nullo di ogni elemento con un dato significativo. Segue un esempio di come inizializzare un array di char:

    • alfabeto [0] = ‘a’;
      alfabeto [1] = ‘b’;
      alfabeto [2] = ‘c’;
      alfabeto [3] = ‘d’;
      alfabeto [4] = ‘e’;
      alfabeto [5] = ‘f’;
      alfabeto [6] = ‘g’;
      alfabeto [7] = ‘h’;
      alfabeto [8] = ‘i’;
      alfabeto [9] = ‘l’;
      alfabeto [10] = ‘m’;
      alfabeto [11] = ‘n’;
      alfabeto [12] = ‘o’;
      alfabeto [13] = ‘p’;
      alfabeto [14] = ‘q’;
      alfabeto [15] = ‘r’;
      alfabeto [16] = ‘s’;
      alfabeto [17] = ‘t’;
      alfabeto [18] = ‘u’;
      alfabeto [19] = ‘v’;
      alfabeto [20] = ‘z’;



    36. Sintassi breve

    • Esiste una sintassi breve che permette di dichiarare, creare implicitamente ed inizializzare un array. Ecco come:

      char alfabeto [] = {‘a’, ‘b’, ‘c’, ’d’, ’e’, ’f’, ’g’, ’h’, ’i’, ’l’, ’m’, ’n’, ’o’, ’p’, ’q’, ’r’, ’s’, ’t’, ’u’, ’v’, ’z’};

      oppure:

      char [] alfabeto = {‘a’, ‘b’, ‘c’, ’d’, ’e’, ’f’, ’g’, ’h’, ’i’, ’l’, ’m’, ’n’, ’o’, ’p’, ’q’, ’r’, ’s’, ’t’, ’u’, ’v’, ’z’};
    • Ogni vettore Java possiede una caratteristica molto interessante che permette di ottenere la dimensione effettiva dell’array stesso, ad esempio:

      int dimensione = alfabeto.length;
    Inizializzerà la variabile «dimensione» (nel nostro specifico caso) al valore «21».


    37. Array multidimensionali

    • Per array multidimensionale si intende la possibilità di contenere allinterno di ogni elemento di un vettore (contrassegnato da un indice) un altro array. Segue un esempio:

      int tabellina [] [] = new int [11] [11];
      tabellina [0] [0] = 0;
      tabellina [0] [1] = 0;
      tabellina [0] [2] = 0;
      tabellina [0] [3] = 0;
      tabellina [0] [4] = 0;
      ...
      tabellina [10] [10] = 100;


    • Nel caso dell’esempio proposto all’interno di ognuno degli «11» elementi del primo vettore, sono presenti altri «11» elementi.


    38. Array multidimensionali

    • Inoltre, è necessario specificare che per ogni elemento di un array, è possibile decidere la dimensione dellarray contenuto. Segue un esempio pratico:

      int array [] [] = new int [4] [];
      array [0] = new int[4];
      array [1] = new int[2];
      array [2] = new int[3];
      array [3] = new int[1];
      array [0] [0] = 3;
      array [0] [1] = 8;
      array [0] [2] = 2;
      array [0] [3] = 1;
      array [1] [0] = 5;
      array [1] [1] = 4;