Java/Strutture di controllo

Java

Linguaggio Java
Linguaggio Java

categoria · sviluppo · modifica
Come posso contribuire?

→ Vai su Wikiversity

Java:  Guida alla programmazioneJava/Introduzione - Specifica di linguaggio - Libreria standardJava/Comunicazione seriale

Tipi di datoJava/Tipi di datoEspressioniJava/OperatoriVariabiliJava/Variabili localiIstruzioniJava/IstruzioniStrutture di controlloJava/Strutture di controlloPackageJava/Package
Classi e oggettiJava/Classi e oggettiOggettiJava/OggettiMetodiJava/MetodiMetodi (cont.)Java/Metodi/2ArrayJava/Array
Modificatori di accessoJava/Modificatori di accessoEreditarietàJava/Ereditarietà
Gestione delle eccezioniJava/Gestione delle eccezioniGenericsJava/Generics
Thread e parallelismoJava/MultithreadingInterblocco ricontrollatoJava/Interblocco ricontrollato
Alcune differenze con il CJava/Concetti fondamentaliLessicoJava/LessicoGrammaticaJava/Grammatica

Introduzione

modifica

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

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

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;

Calcola una certa espressione il cui compile-time type sia uno dei seguenti:

  1. char, byte, short, int
  2. Character, Byte, Short, Integer
  3. java.lang.Enum o sottotipo
  4. java.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

  1. apporre sempre il break; prima delle etichette case e default
  2. 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

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

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;

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
  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
  Per approfondire, vedi Gestione delle eccezioni.
  1. 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
  1. Metodo String.equals(Object)
Approfondimenti
  1. Java Language Specification, 17.3
  2. Strings in switch Statements