Java/Strutture di controllo
Introduzione
[modifica | modifica sorgente]In virtù del teorema di Böhm-Jacopini, ogni algoritmo può essere espresso in un linguaggio di programmazione che disponga di almeno tre strutture di controllo:
- almeno una struttura di sequenza;
- almeno una struttura selettiva;
- almeno una struttura iterativa.
Il Java dispone di queste strutture e anche di una struttura aggiuntiva per la gestione delle eccezioni.
Sequenza delle istruzioni
[modifica | modifica sorgente]Non esiste una sintassi specifica per la struttura di sequenza. La successione delle istruzioni è determinata dall'ordine in cui sono scritte nel codice sorgente del programma.
Sotto certe condizioni, il compilatore può cambiare l'ordine delle istruzioni localmente ad un metodo, e ciò ha conseguenze nei programmi multithreaded in cui i thread non sincronizzano correttamente gli accessi alle risorse condivise.[A 1]
Invece, il programmatore non può sovvertire l'ordine in cui le istruzioni sono eseguite: l'istruzione goto
, presente in altri linguaggi, è stata deliberatamente lasciata fuori dal Java.
Strutture selettive
[modifica | modifica sorgente]if
[modifica | modifica sorgente]Calcola una certa espressione booleana, ed esegue una scelta tra due istruzioni in base al valore ottenuto. Spesso una delle due istruzioni (o entrambe) sono in realtà dei blocchi di codice.
if (condizione)
istruzione;
// se la condizione restituisce false
, non viene eseguito nulla
Oppure:
if (condizione) istruzione; else istruzione;
Si può realizzare una catena di if ... else if
, come in C:
if (condizione) istruzione; else if(condizione) istruzione; else if (condizione) istruzione; else istruzione;
switch
[modifica | modifica sorgente]Calcola una certa espressione il cui compile-time type sia uno dei seguenti:
char
,byte
,short
,int
Character
,Byte
,Short
,Integer
java.lang.Enum
o sottotipojava.lang.String
ed esegue una scelta tra n vie, in base al valore calcolato.
switch(espressione) { case espr_costante1: istruzioni; case espr_costante2: istruzioni; ... default: istruzioni; }
La clausola default è facoltativa e indica le istruzioni che saranno eseguite se il valore calcolato non corrisponde a nessuno tra quelli indicati nelle etichette case
. Non deve per forza stare in fondo al blocco, quindi può trovarsi anche all'inizio o in mezzo.
I valori che accompagnano le etichette case
devono essere espressioni costanti e diverse tra loro.[1]
- L'espressione
Se l'espressione esaminata dallo switch è null
, allora viene lanciata una NullPointerException.
Se il tipo dell'espressione è String
, allora i confronti tra le stringhe sono svolti come specificato dal metodo String.equals
[D 1], quindi in modo case sensitive[A 2].
- Fall-through
Raggiunta una etichetta case
ed eseguite le relative istruzioni, il flusso di esecuzione prosegue attraversando le etichette case seguenti, a meno che non incontri una istruzione break;
(nel qual caso esce dallo switch
). Nell'esempio riportato sopra, se il valore di espressione è uguale a espr_costante1, allora saranno eseguite le istruzioni di tutte le etichette case
e anche dell'etichetta default
.
Nella maggior parte dei casi, questo comportamento, perfettamente legittimo dal punto di vista del linguaggio, non era nelle intenzioni del programmatore che ha scritto il codice. Quindi, è buona norma
- apporre sempre il
break;
prima delle etichettecase
edefault
- in caso contrario, segnalare con un commento che il
break;
è stato omesso intenzionalmente
Quindi, tipicamente, una istruzione switch
ha la forma
switch(espressione) { case espr_costante1: istruzioni; break; case espr_costante2: istruzioni; break; ... default: istruzioni; break; // facoltativo }
Strutture iterative
[modifica | modifica sorgente]while
[modifica | modifica sorgente]Esegue una istruzione un numero arbitrario di volte, finché una certa espressione booleana restituisce true
.
while(condizione) istruzione;
La condizione viene calcolata ogni volta prima di eseguire l'istruzione.
do ... while
[modifica | modifica sorgente]Esegue una istruzione un numero arbitrario di volte, finché una certa espressione booleana restituisce true
.
do istruzione; while(condizione);
La condizione viene calcolata ogni volta dopo che l'istruzione è stata eseguita.
A differenza del while
, il do ... while
esegue l'istruzione almeno una volta. In pratica, è equivalente a:
istruzione; while(condizione) istruzione;
for
[modifica | modifica sorgente]Esegue una istruzione un numero arbitrario di volte, in funzione di una espressione booleana, e permette di indicare in un solo punto le variabili e le istruzioni che influenzano l'esecuzione del ciclo.
for(inizializzazione; condizione; passo) istruzione;
inizializzazione, condizione e passo sono facoltative; i punti e virgola che le separano sono obbligatori.
La prima contiene una istruzione, oppure la dichiarazione di una o più variabili, e (se presente) viene eseguita una solta volta, all'entrata del for
. La seconda è una espressione booleana, che viene calcolata ad ogni ciclo (se omessa, si assume true
, il che risulta utile per scrivere in modo conciso un ciclo infinito). La terza contiene una istruzione che viene eseguita alla fine di ogni ciclo.
In definitiva, il for
si comporta come il codice seguente:
{ inizializzazione; while(condizione) { istruzione; passo; } }
Il vantaggio principale di mettere insieme inizializzazione, condizione e passo è la maggiore leggibilità del codice; inoltre, modificare le variabili e la condizione che controllano il ciclo è più semplice, in quanto la zona di codice interessata è circoscritta e in evidenza.
- Scope delle variabili
Lo scope delle variabili definite nell'inizializzazione coincide con l'intera istruzione for
(cioè comprende l'istruzione, la condizione e il passo); da ciò consegue che il seguente codice compila:
// qui non esiste alcuna variabile i for(int i = 0; i < 10; i++); // qui non esiste alcuna variabile i for(int i = 0; i > -10; i--); // usa una nuova variabile i, diversa da quella usata nel primo ciclo for // qui non esiste alcuna variabile i
- Uso tipico
In genere, viene usato dove ci sono una o più variabili che controllano l'andamento del ciclo. In genere, l'inizializzazione viene usata tipicamente per dichiarare le variabili e inizializzarle e il passo le modifica. Risulta molto comodo per iterare su array o oggetti contenitore (collezioni).
Un esempio con più di una variabile di controllo:
for (int i = 0, y = 20; i < 10 && y > 16; i = i + 2, y--) {
System.out.println("il valore di i è: " + i + " il valore di y è: " + y);
}
il cui output è:
il valore di i è: 0 il valore di y è: 20
il valore di i è: 2 il valore di y è: 19
il valore di i è: 4 il valore di y è: 18
il valore di i è: 6 il valore di y è: 17
in quanto la condizione di terminazione restituisce true con i < 10 e y > 16, ma alla quinta iterazione y = 16 e restituisce false terminando il ciclo.
For each
[modifica | modifica sorgente]Per approfondire, vedi Java/Array. |
Itera lungo un array o un oggetto di tipo java.lang.Iterable
.
for(NomeTipo nomeVariabile : contenitore) istruzione;
Gestione delle eccezioni
[modifica | modifica sorgente]Per approfondire, vedi Gestione delle eccezioni. |
Note
[modifica | modifica sorgente]- ↑ Ciò si rende necessario per evitare il caso in cui due etichette diverse assumano un valore uguale a tempo di esecuzione; infatti, in questo caso, il flusso di esecuzione dovrebbe prendere due strade diverse, il che è impossibile. Al contrario, se tutte le espressioni sono costanti, il compilatore può controllare che siano anche diverse tra loro e, quindi, evitare questa situazione di ambiguità.
- Documentazione
- Approfondimenti