Variabili in Java
- Le variabili in Java sono unità fondamentali di memorizzazione, definite da un identificatore, un tipo e un inizializzatore opzionale.
- Le variabili devono essere dichiarate prima dell'uso, specificando il tipo e l'identificatore.
- L'inizializzazione dinamica consente di assegnare valori alle variabili utilizzando espressioni valide al momento della dichiarazione.
- L'ambito di una variabile definisce la sua visibilità e durata, con ambiti principali definiti da classi e metodi.
Variabili
La variabile è l'unità fondamentale di memorizzazione in un programma Java.
Una variabile è definita dalla combinazione di un identificatore, un tipo e un inizializzatore opzionale. Inoltre, tutte le variabili possiedono un ambito, che ne definisce la visibilità, e una durata. Questi elementi vengono esaminati di seguito.
Dichiarare una Variabile
In Java, tutte le variabili devono essere dichiarate prima di poter essere utilizzate. La forma di base di una dichiarazione di variabile è la seguente:
tipo identificatore [ = valore ][, identificatore [ = valore ] ...];
Qui, tipo
è uno dei tipi atomici di Java, oppure il nome di una classe o di un'interfaccia. (I tipi di classe e di interfaccia verranno trattati più avanti, nelle prossime lezioni).
identificatore
è il nome della variabile. È possibile inizializzare la variabile specificando un segno di uguale e un valore. Occorre ricordare che l'espressione di inizializzazione deve produrre un valore dello stesso tipo (o di tipo compatibile) di quello specificato per la variabile. Per dichiarare più di una variabile dello stesso tipo, utilizzare un elenco separato da virgole.
Ecco alcuni esempi di dichiarazioni di variabili di vari tipi. Si noti che alcune includono un'inizializzazione.
int a, b, c; // dichiara tre interi: a, b e c.
int d = 3, e = 5; // dichiara altri due interi, inizializzando d ed e.
byte z = 22; // inizializza z.
double pi = 3.14159; // dichiara un'approssimazione di pi greco.
char x = 'x'; // la variabile x ha il valore 'x'.
Gli identificatori scelti non possiedono nulla di intrinseco nel loro nome che ne indichi il tipo. Java consente a qualsiasi identificatore correttamente formato di avere qualunque tipo dichiarato.
Inizializzazione Dinamica
Sebbene gli esempi precedenti abbiano usato solo costanti come inizializzatori, Java consente di inizializzare dinamicamente le variabili, utilizzando qualsiasi espressione valida al momento della dichiarazione della variabile.
Ad esempio, ecco un breve programma che calcola la lunghezza dell'ipotenusa di un triangolo rettangolo dati i due cateti:
// Dimostrazione dell'inizializzazione dinamica.
class InizDin {
public static void main(String[] argomenti) {
double a = 3.0, b = 4.0;
// c viene inizializzata dinamicamente
double c = Math.sqrt(a * a + b * b);
System.out.println("L'ipotenusa è " + c);
}
}
In questo caso vengono dichiarate tre variabili locali: a
, b
e c
.
Le prime due, a
e b
, sono inizializzate tramite costanti. c
, invece, è inizializzata dinamicamente alla lunghezza dell'ipotenusa (usando il teorema di Pitagora).
Il programma utilizza un altro metodo integrato di Java, sqrt()
, che è membro della classe Math
, per calcolare la radice quadrata del suo argomento. Il punto chiave è che l'espressione di inizializzazione può usare qualsiasi elemento valido al momento dell'inizializzazione, incluse chiamate a metodi, altre variabili o letterali.
Ambito e Durata delle Variabili
Finora tutte le variabili utilizzate sono state dichiarate all'inizio del metodo main()
. Tuttavia, Java permette di dichiarare variabili all'interno di qualsiasi blocco.
Come spiegato nelle lezioni precedenti, un blocco inizia con una parentesi graffa di apertura e termina con una parentesi graffa di chiusura. Un blocco definisce un ambito. Pertanto, ogni volta che viene aperto un nuovo blocco, viene creato un nuovo ambito. Un ambito stabilisce quali oggetti sono visibili alle altre parti del programma e determina anche la durata di tali oggetti.
Non è raro pensare a due categorie generali di ambiti: globale e locale.
Tuttavia, questi ambiti tradizionali non si adattano bene al modello rigoroso e orientato agli oggetti di Java. Sebbene sia possibile creare ciò che equivale ad un ambito globale, si tratta di un'eccezione piuttosto rara.
In Java, i due ambiti principali sono quelli definiti da una classe e quelli definiti da un metodo. Anche questa distinzione è in parte artificiale. Tuttavia, poiché l'ambito di classe presenta proprietà e attributi unici che non si applicano a quello definito da un metodo, la distinzione ha un certo senso.
A causa di tali differenze, la discussione sull'ambito di classe (e sulle variabili dichiarate al suo interno) è rimandata alle prossime lezioni, dove verranno descritte le classi. Per il momento verranno esaminati soltanto gli ambiti definiti da o all'interno di un metodo.
L'ambito definito da un metodo inizia con la sua parentesi graffa di apertura. Se il metodo possiede parametri, anch'essi sono inclusi nell'ambito del metodo. L'ambito di un metodo termina con la sua parentesi graffa di chiusura. Questo blocco di codice è chiamato corpo del metodo.
Come regola generale, le variabili dichiarate all'interno di un ambito non sono visibili (ossia accessibili) al codice definito al di fuori di tale ambito. Dunque, dichiarando una variabile all'interno di un ambito, la si localizza e la si protegge da accessi e/o modifiche non autorizzati. In effetti, le regole sull'ambito forniscono le basi dell'incapsulamento. Una variabile dichiarata all'interno di un blocco è detta variabile locale.
Gli ambiti possono essere nidificati. Ogni volta che viene creato un blocco di codice, si crea un nuovo ambito nidificato. In tal caso, l'ambito esterno racchiude l'ambito interno. Ciò significa che gli oggetti dichiarati nell'ambito esterno saranno visibili al codice presente nell'ambito interno. Il contrario non è vero: gli oggetti dichiarati nell'ambito interno non saranno visibili all'esterno.
Per comprendere l'effetto degli ambiti nidificati, si consideri il seguente programma:
// Dimostrazione dell'ambito dei blocchi.
class Ambito {
public static void main(String[] argomenti) {
int x; // noto a tutto il codice all'interno di main
x = 10;
if(x == 10) { // inizio di un nuovo ambito
int y = 20; // noto solo a questo blocco
// x e y sono entrambi noti qui.
System.out.println("x e y: " + x + " " + y);
x = y * 2;
}
// y = 100; // Errore! y non è noto qui
// x è ancora noto qui.
System.out.println("x vale " + x);
}
}
Come indicato nei commenti, la variabile x
viene dichiarata all'inizio dell'ambito di main()
ed è accessibile a tutto il codice successivo all'interno di main()
. All'interno del blocco if
viene dichiarata y
. Poiché un blocco definisce un ambito, y
è visibile solo ad altro codice all'interno di quel blocco. Per questo motivo, fuori dal suo blocco, la riga y = 100;
è commentata. Rimuovendo il simbolo di commento si otterrebbe un errore in fase di compilazione, perché y
non è visibile al di fuori del suo blocco. All'interno del blocco if
, x
può essere usata perché il codice di un blocco (ossia di un ambito nidificato) ha accesso alle variabili dichiarate da un ambito circostante.
All'interno di un blocco le variabili possono essere dichiarate in qualsiasi punto, ma sono valide solo dopo la loro dichiarazione. Pertanto, se una variabile viene definita all'inizio di un metodo, essa è disponibile per tutto il codice all'interno di quel metodo. Al contrario, se una variabile viene dichiarata alla fine di un blocco, risulta di fatto inutile, poiché nessun codice potrà accedervi. Ad esempio, il seguente frammento è invalido perché conteggio
non può essere utilizzato prima della sua dichiarazione:
// Questo frammento è errato!
conteggio = 100; // ops! impossibile usare conteggio prima che sia dichiarato!
int conteggio;
Ecco un altro punto importante da ricordare: le variabili vengono create quando il loro ambito viene attivato ed eliminate quando se ne esce.
Ciò significa che una variabile non manterrà il proprio valore una volta uscito dall'ambito. Pertanto, le variabili dichiarate all'interno di un metodo non conserveranno i loro valori tra le diverse chiamate a quel metodo. Inoltre, una variabile dichiarata all'interno di un blocco perderà il proprio valore quando si lascia il blocco. Di conseguenza, la durata di una variabile è limitata al suo ambito.
Se una dichiarazione di variabile include un inizializzatore, quella variabile verrà reinizializzata ogni volta che si entra nel blocco in cui è dichiarata. Per esempio, si consideri il programma seguente:
// Dimostrare la durata di una variabile.
class DurataVita {
public static void main(String[] argomenti) {
int x;
for (x = 0; x < 3; x++) {
int y = -1; // y viene inizializzata ogni volta che si entra nel blocco
System.out.println("y vale: " + y); // stampa sempre -1
y = 100;
System.out.println("ora y vale: " + y);
}
}
}
L'output generato da questo programma è il seguente:
y vale: -1
ora y vale: 100
y vale: -1
ora y vale: 100
y vale: -1
ora y vale: 100
Come si può vedere, y
viene reinizializzata a –1
ogni volta che il ciclo for
interno viene eseguito. Anche se successivamente le viene assegnato il valore 100, tale valore viene perso.
Un ultimo punto: benché i blocchi possano essere nidificati, non è possibile dichiarare una variabile con lo stesso nome di una presente in un ambito esterno. Per esempio, il programma seguente è illegale:
// Questo programma non verrà compilato
class ErroreAmbito {
public static void main(String[] argomenti) {
int barra = 1;
{ // crea un nuovo ambito
int barra = 2; // Errore in fase di compilazione – barra già definita!
}
}
}