2. Le basi del linguaggio Java
2.1. Introduzione
Inizialmente tratteremo Java come un linguaggio di programmazione tradizionale. Tratteremo gli oggetti in seguito.
In un programma, ci sono due cose
- i dati
- le istruzioni che li manipolano
In genere ci sforziamo di separare i dati dalle istruzioni:

2.2. Dati Java
Java utilizza i seguenti tipi di dati:
- interi
- numeri in virgola mobile
- caratteri e stringhe
- Booleani
- oggetti
2.2.1. Tipi di dati predefiniti
Tipo | Codifica | Intervallo |
char | 2 byte | Carattere Unicode |
int | 4 byte | [-231, 231 -1] |
long | 8 byte | [-263, 263 -1] |
byte | 1 byte | [-27 , 27 -1] |
short | 2 byte | [-2(15), 2(15),-1] |
float | 4 byte | [3,410⁻³⁸, 3,410³⁸] in valore assoluto |
doppio | 8 byte | [1,7 × 10⁻³⁰⁸, 1,7 × 10³⁰⁸] in valore assoluto |
booleano | 1 bit | vero, falso |
Stringa | riferimento a un oggetto | stringa |
Data | riferimento a un oggetto | data |
Carattere | riferimento all'oggetto | carattere |
Intero | riferimento a un oggetto | int |
Long | riferimento a un oggetto | long |
Byte | riferimento a oggetto | byte |
Float | riferimento a oggetto | float |
doppio | riferimento a un oggetto | double |
Booleano | riferimento a un oggetto | Booleano |
2.2.2. Notazione dei dati letterali
intero | 145, -7, 0xFF (esadecimale) |
doppio | 134,789, -45E-18 (-45 × 10⁻¹⁸) |
float | 134,789F, -45E-18F (-45 × 10⁻¹⁸) |
carattere | 'A', 'b' |
stringa | "oggi" |
booleano | vero, falso |
data | new Date(13,10,1954) (giorno, mese, anno) |
2.2.3. Dichiarazione dei dati
2.2.3.1. Ruolo delle dichiarazioni
Un programma manipola dati caratterizzati da un nome e da un tipo. Questi dati vengono memorizzati nella memoria. Quando il programma viene compilato, il compilatore assegna a ciascun dato una posizione di memoria caratterizzata da un indirizzo e da una dimensione. Lo fa utilizzando le dichiarazioni effettuate dal programmatore.
Inoltre, queste dichiarazioni consentono al compilatore di rilevare errori di programmazione. Pertanto, l'operazione
x=x*2;
sarà dichiarata errata se x è una stringa, ad esempio.
2.2.3.2. Dichiarazione delle costanti
La sintassi per dichiarare una costante è la seguente:
<mark style="background-color: #ffff00">tipo</mark>**<mark style="background-color: #ffff00"> finale </mark>**<mark style="background-color: #ffff00">nome=valore; </mark> //definisce una costante nome=valore
es.: final float PI=3.141592F;
Nota
Perché dichiarare le costanti?
- Il programma sarà più facile da leggere se alla costante viene assegnato un nome significativo:
es.: *final float VAT\_rate=0.186F*;
- Modificare il programma sarà più semplice se la "costante" deve essere cambiata. Pertanto, nel caso precedente, se l'aliquota IVA cambia al 33%, l'unica modifica necessaria sarà quella di cambiare l'istruzione che ne definisce il valore:
*final float aliquota\_fiscale=0.33F*;
Se avessimo utilizzato esplicitamente 0,186 nel programma, avremmo dovuto modificare numerose istruzioni.
2.2.3.3. Dichiarazione delle variabili
Una variabile è identificata da un nome e fa riferimento a un tipo di dati. Un nome di variabile Java è composto da n caratteri, il primo dei quali deve essere una lettera, mentre gli altri possono essere lettere o numeri. Java distingue tra lettere maiuscole e minuscole. Pertanto, le variabili FIN e fin sono diverse.
Le variabili possono essere inizializzate al momento della loro dichiarazione. La sintassi per dichiarare una o più variabili è:
dove identificatore_di_tipo è un tipo predefinito o un tipo di oggetto definito dal programmatore.
2.2.4. Conversioni tra numeri e stringhe di caratteri
numero -> stringa | "" + numero |
stringa -> int | Integer.parseInt(stringa) |
stringa -> long | Long.parseLong(stringa) |
stringa -> double | Double.valueOf(stringa).doubleValue() |
stringa -> float | Float.valueOf(string).floatValue() |
Ecco un programma che illustra le principali tecniche di conversione tra numeri e stringhe. La conversione di una stringa in un numero potrebbe non riuscire se la stringa non rappresenta un numero valido. Ciò provoca un errore irreversibile, noto come eccezione in Java. Questo errore può essere gestito utilizzando il seguente blocco try/catch:
try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Se la funzione non genera un'eccezione, il programma procede all'istruzione successiva; altrimenti, entra nel corpo della clausola catch e poi procede all'istruzione successiva. Torneremo più avanti alla gestione delle eccezioni.
import java.io.*;
public class conv1{
public static void main(String arg[]){
String S;
final int i=10;
final long l=100000;
final float f=(float)45.78;
double d=-14.98;
// number --> string
S=""+i;
affiche(S);
S=""+l;
affiche(S);
S=""+f;
affiche(S);
S=""+d;
affiche(S);
//boolean --> string
final boolean b=false;
S=""+new Boolean(b);
affiche(S);
// string --> int
int i1;
i1=Integer.parseInt("10");
affiche(""+i1);
try{
i1=Integer.parseInt("10.67");
affiche(""+i1);
} catch (Exception e){
affiche("Erreur "+e);
}
// string --> long
long l1;
l1=Long.parseLong("100");
affiche(""+l1);
try{
l1=Long.parseLong("10.675");
affiche(""+l1);
} catch (Exception e){
affiche("Erreur "+e);
}
// chain --> double
double d1;
d1=Double.valueOf("100.87").doubleValue();
affiche(""+d1);
try{
d1=Double.valueOf("abcd").doubleValue();
affiche(""+d1);
} catch (Exception e){
affiche("Erreur "+e);
}
// string --> float
float f1;
f1=Float.valueOf("100.87").floatValue();
affiche(""+f1);
try{
d1=Float.valueOf("abcd").floatValue();
affiche(""+f1);
} catch (Exception e){
affiche("Erreur "+e);
}
}// fine hand
public static void affiche(String S){
System.out.println("S="+S);
}
}// end of class
I risultati sono i seguenti:
S=10
S=100000
S=45.78
S=-14.98
S=false
S=10
S=Erreur java.lang.NumberFormatException: 10.67
S=100
S=Erreur java.lang.NumberFormatException: 10.675
S=100.87
S=Erreur java.lang.NumberFormatException: abcd
S=100.87
S=Erreur java.lang.NumberFormatException: abcd
2.2.5. Matrici di dati
Un array Java è un oggetto che consente di raggruppare dati dello stesso tipo sotto un unico identificatore. Viene dichiarato come segue:
Tipo Array[] = new Tipo[n] oppure Tipo[] Array = new Tipo[n]
Entrambe le sintassi sono valide. n è il numero di elementi che l'array può contenere. La sintassi Array[i] si riferisce all'elemento all'indice i, dove i è compreso nell'intervallo [0,n-1]. Qualsiasi riferimento all'elemento Array[i] dove i non è compreso nell'intervallo [0,n-1] causerà un'eccezione.
Un array bidimensionale può essere dichiarato come segue:
Type Array[][] = new Type[n][p] oppure Type[][] Array = new Type[n][p]
La sintassi Array[i] si riferisce all'elemento i di Array, dove i appartiene all'intervallo [0,n-1]. Array[i] è esso stesso un array: Array[i][j] si riferisce al j-esimo elemento di Array[i], dove j appartiene all'intervallo [0,p-1]. Qualsiasi riferimento a un elemento di Array con indici errati genera un errore fatale.
Ecco un esempio:
public class test1{
public static void main(String arg[]){
float[][] taux=new float[2][2];
taux[1][0]=0.24F;
taux[1][1]=0.33F;
System.out.println(taux[1].length);
System.out.println(taux[1][1]);
}
}
e i risultati dell'esecuzione:
Un array è un oggetto dotato di un attributo "length": si tratta della dimensione dell'array.
2.3. Comandi Java di base
Distinguiamo
- le istruzioni di base eseguite dal computer.
- istruzioni che controllano il flusso del programma.
Le istruzioni di base diventano chiare se si considera la struttura di un microcomputer e delle sue periferiche.

-
Lettura delle informazioni dalla tastiera
-
Elaborazione delle informazioni
-
Scrittura delle informazioni sullo schermo
-
Lettura delle informazioni da un file su disco
-
Scrivere informazioni su un file su disco
2.3.1. Scrivere sullo schermo
La sintassi dell'istruzione di output su schermo è la seguente:
System.out.println(espressione) oppure System.err.println(espressione)
dove espressione è un tipo di dati qualsiasi che può essere convertito in una stringa da visualizzare sullo schermo. Nell'esempio precedente abbiamo visto due istruzioni di stampa:
System.out scrive su un file di testo, che per impostazione predefinita è lo schermo. Lo stesso vale per System.err. A questi file sono assegnati rispettivamente i numeri (o descrittori) 1 e 2. Anche il flusso di input della tastiera (System.in) viene trattato come un file di testo, con descrittore 0. Sia DOS che Unix supportano il piping dei comandi:
Tutto ciò che command1 scrive su System.out viene convogliato (reindirizzato) all'ingresso System.in di command2. In altre parole, command2 legge da System.in i dati prodotti da command2 tramite System.out, che quindi non vengono più visualizzati sullo schermo. Questo sistema è ampiamente utilizzato in Unix. In questo piping, il flusso System.err non viene reindirizzato: scrive sullo schermo. Questo è il motivo per cui viene utilizzato per scrivere messaggi di errore (da cui il nome err): possiamo essere certi che quando i comandi vengono sottoposti a piping, i messaggi di errore continueranno ad apparire sullo schermo. Dovremmo quindi prendere l'abitudine di scrivere i messaggi di errore sullo schermo utilizzando il flusso System.err piuttosto che il flusso System.out.
2.3.2. Lettura dei dati digitati sulla tastiera
Il flusso di dati proveniente dalla tastiera è rappresentato dall'oggetto System.in di tipo InputStream. Questo tipo di oggetto consente di leggere i dati carattere per carattere. Spetta poi al programmatore estrarre le informazioni rilevanti da questo flusso di caratteri. Il tipo InputStream non consente di leggere un'intera riga di testo in una sola volta. Il tipo BufferedReader lo consente tramite il metodo readLine.
Per leggere le righe di testo immesse tramite la tastiera, creiamo un nuovo flusso di input di tipo BufferedReader a partire dal flusso di input System.in* di tipo InputStream*:
Non spiegheremo qui i dettagli di questa istruzione, poiché riguarda il concetto di costruzione di oggetti. La useremo così com'è.
La creazione di uno stream può fallire: in tal caso viene generato un errore fatale, chiamato eccezione in Java. Ogni volta che un metodo è suscettibile di generare un'eccezione, il compilatore Java richiede che questa venga gestita dal programmatore. Pertanto, per creare lo stream di input precedente, dobbiamo effettivamente scrivere:
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
System.err.println("Erreur " +e);
System.exit(1);
}
Anche in questo caso, non entreremo nei dettagli della gestione delle eccezioni. Una volta creato il flusso IN precedente, possiamo leggere una riga di testo utilizzando l'istruzione:
La riga digitata sulla tastiera viene memorizzata nella variabile ligne e può quindi essere utilizzata dal programma.
2.3.3. Esempio di input/output
Ecco un programma che illustra le operazioni di input/output da tastiera e schermo:
import java.io.*; // required to use I/O streams
public class io1{
public static void main (String[] arg){
// write to System.out stream
Object obj=new Object();
System.out.println(""+obj);
System.out.println(obj.getClass().getName());
// write to System.err stream
int i=10;
System.err.println("i="+i);
// reading a line entered on the keyboard
String ligne;
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
affiche(e);
System.exit(1);
}
System.out.print("Tapez une ligne : ");
try{
ligne=IN.readLine();
System.out.println("ligne="+ligne);
} catch (Exception e){
affiche(e);
System.exit(2);
}
}//fine hand
public static void affiche(Exception e){
System.err.println("Erreur : "+e);
}
}//end of class
e i risultati dell'esecuzione:
C:\Serge\java\bases\iostream>java io1
java.lang.Object@1ee78b
java.lang.Object
i=10
Tapez une ligne : je suis là
ligne=je suis là
Le istruzioni
hanno lo scopo di mostrare che qualsiasi oggetto può essere visualizzato. Non tenteremo qui di spiegare il significato di ciò che viene visualizzato. Nel blocco abbiamo anche la visualizzazione del valore di un oggetto:
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
affiche(e);
System.exit(1);
}
La variabile e è un oggetto Exception che viene visualizzato qui utilizzando la chiamata display(e). Abbiamo incontrato questa visualizzazione del valore di un'eccezione nel programma di conversione visto in precedenza, anche se all'epoca non ne abbiamo discusso.
2.3.4. Assegnazione del valore di un'espressione a una variabile
In questo caso ci interessa l'operazione variabile=espressione;
L'espressione può essere dei seguenti tipi: aritmetica, relazionale, booleana, stringa
2.3.4.1. Interpretazione dell'operazione di assegnazione
L'operazione variabile=espressione; è essa stessa un'espressione la cui valutazione procede come segue:
- Viene valutato il lato destro dell'assegnazione: il risultato è un valore V.
- Il valore V viene assegnato alla variabile
- Il valore V è anche il valore dell'assegnazione, ora considerata come un'espressione.
Ecco perché l’espressione V1=V2=espressione è valida. A causa della precedenza, verrà valutato l'operatore = più a destra. Abbiamo quindi V1=(V2=espressione). L'espressione V2=espressione viene valutata e ha il valore V. La valutazione di questa espressione ha causato l'assegnazione di V a V2. L'operatore = successivo viene quindi valutato come V1=V. Il valore di questa espressione è ancora V. La sua valutazione fa sì che V venga assegnato a V1. Pertanto, l'operazione V1=V2=espressione è un'espressione la cui valutazione
1: fa sì che il valore di espressione venga assegnato alle variabili V1 e V2
2: restituisce il valore di espressione come risultato.
Possiamo generalizzare questo concetto in un'espressione della forma: V1=V2=....=Vn=espressione
2.3.4.2. Espressione aritmetica
Gli operatori per le espressioni aritmetiche sono i seguenti:
+: addizione
- : sottrazione
*: moltiplicazione
/ : divisione: il risultato è il quoziente esatto se almeno uno degli operandi è reale. Se entrambi gli operandi sono interi, il risultato è il quoziente intero. Quindi, 5/2 -> 2 e 5.0/2 -> 2.5.
% : divisione: il risultato è il resto indipendentemente dalla natura degli operandi, con il quoziente che è un numero intero. Si tratta quindi dell'operazione modulo.
Esistono varie funzioni matematiche:
double sqrt(double x) | radice quadrata |
double cos(double x) | coseno |
doppio sin(doppio x) | Seno |
tangente | Tangente |
double pow(double x, double y) | x elevato a y (x > 0) |
doppio exp(doppio x) | Esponenziale |
log(x) | logaritmo naturale |
doppio abs(doppio x) | valore assoluto |
ecc...
Tutte queste funzioni sono definite in una classe Java chiamata Math. Quando le si utilizza, è necessario anteporre loro il nome della classe in cui sono definite. Pertanto, si scriverebbe:
La definizione della classe Math è la seguente:
public final class java.lang.Math
extends java.lang.Object (I-§1.12)
{
// Fields
public final static double E; §1.10.1
public final static double PI; §1.10.2
// Methods
public static double abs(double a); §1.10.3
public static float abs(float a); §1.10.4
public static int abs(int a); §1.10.5
public static long abs(long a); §1.10.6
public static double acos(double a); §1.10.7
public static double asin(double a); §1.10.8
public static double atan(double a); §1.10.9
public static double atan2(double a, double b); §1.10.10
public static double ceil(double a); §1.10.11
public static double cos(double a); §1.10.12
public static double exp(double a); §1.10.13
public static double floor(double a); §1.10.14
public static double §1.10.15
IEEEremainder(double f1, double f2);
public static double log(double a); §1.10.16
public static double max(double a, double b); §1.10.17
public static float max(float a, float b); §1.10.18
public static int max(int a, int b); §1.10.19
public static long max(long a, long b); §1.10.20
public static double min(double a, double b); §1.10.21
public static float min(float a, float b); §1.10.22
public static int min(int a, int b); §1.10.23
public static long min(long a, long b); §1.10.24
public static double pow(double a, double b); §1.10.25
public static double random(); §1.10.26
public static double rint(double a); §1.10.27
public static long round(double a); §1.10.28
public static int round(float a); §1.10.29
public static double sin(double a); §1.10.30
public static double sqrt(double a); §1.10.31
public static double tan(double a); §1.10.32
}
2.3.4.3. Operatori nella valutazione delle espressioni aritmetiche
La precedenza degli operatori nella valutazione di un'espressione aritmetica è la seguente (dalla più alta alla più bassa):
[funzioni], [ ( )], [ *, /, %], [+, -]
Gli operatori all'interno dello stesso blocco [ ] hanno la stessa precedenza.
2.3.4.4. Operatori relazionali
Gli operatori sono i seguenti: <, <=, ==, !=, >, >=
Ordine di precedenza
>, >=, <, <=
==, !=
Il risultato di un'espressione relazionale è il valore booleano false se l'espressione è falsa; in caso contrario, è true.
Esempio:
Confronto tra due caratteri
Siano dati due caratteri C1 e C2. È possibile confrontarli utilizzando gli operatori
<, <=, ==, !=, >, >=
In questo caso, vengono confrontati i loro codici ASCII, che sono numeri. Ricordiamo che, secondo l'ordine ASCII, valgono le seguenti relazioni:
spazio < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. < 'z'
Confronto tra due stringhe di caratteri
Vengono confrontate carattere per carattere. La prima disuguaglianza riscontrata tra due caratteri determina una disuguaglianza nella stessa direzione per le stringhe.
Esempi:
Si consideri il confronto tra le stringhe "Cat" e "Dog"
Quest'ultima disuguaglianza ci permette di concludere che "Cat" < "Dog".

Consideriamo il confronto tra le stringhe "Cat" e "Kitten". Sono uguali per tutta la lunghezza fino a quando la stringa "Cat" non è esaurita. In questo caso, la stringa esaurita viene dichiarata quella "più piccola". Abbiamo quindi la relazione
"Cat" < "Kitten".
Funzioni per il confronto tra due stringhe
Qui non possiamo usare gli operatori relazionali <, <=, ==, !=, >, >=. Dobbiamo usare i metodi della classe String:
String chaine1, chaine2;
chaine1=…;
chaine2=…;
int i=chaine1.compareTo(chaine2);
boolean egal=chaine1.equals(chaine2)
Nel codice sopra riportato, la variabile i avrà il valore:
0: se le due stringhe sono uguali
1: se stringa 1 > stringa 2
-1: se stringa 1 < stringa 2
La variabile equal avrà il valore true se le due stringhe sono uguali.
2.3.4.5. Espressioni booleane
Gli operatori sono & (e), || (o) e ! (non). Il risultato di un'espressione booleana è un valore booleano.
Ordine di precedenza ! , &&, ||
esempio:
Gli operatori relazionali hanno precedenza sugli operatori && e ||.
2.3.4.6. Operazioni bit a bit
Gli operatori
Siano i e j due numeri interi.
i<<n | sposta i di n bit a sinistra. I bit in entrata sono zeri. |
i>>n | sposta i di n bit a destra. Se i è un numero intero con segno (signed char, int, long), il bit di segno viene mantenuto. |
i & j | Esegue l'operazione logica AND tra i e j bit per bit. |
i | j | esegue l'OR logico di i e j bit per bit. |
~i | completa i a 1 |
i^j | esegue l'operazione XOR tra i e j |
Sia
operazione | valore |
i<<4 | 0x23F0 |
i>>4 | 0x0123 il bit di segno viene mantenuto. |
k>>4 | 0xFF12 il bit di segno viene mantenuto. |
i&j | 0x1023 |
i|j | 0xF33F |
~i | 0xEDC0 |
2.3.4.7. Combinazione di operatori
a=a+b può essere scritto come a+=b
a=a-b può essere scritto come a-=b
Lo stesso vale per gli operatori /, %, *, <<, >>, &, |, ^
Pertanto, a=a+2; può essere scritto come a+=2;
2.3.4.8. Operatori di incremento e decremento
La notazione variable++ significa variable=variable+1 oppure variable+=1
La notazione variable-- significa variable=variable-1 oppure variable-=1.
2.3.4.9. L'operatore ?
L'espressione expr_cond ? expr1:expr2 viene valutata come segue:
1: Viene valutata l'espressione expr_cond. Si tratta di un'espressione condizionale con valore vero o falso
2: Se è vera, il valore dell'espressione è quello di expr1. expr2 non viene valutata.
3: Se è falso, si verifica il contrario: il valore dell'espressione è quello di expr2. expr1 non viene valutato.
Esempio
*i = (j > 4 ? j + 1 : j - 1);*
assegnerà alla variabile i:
j+1 se j>4, j-1 altrimenti
È come scrivere if(j>4) i=j+1; else i=j-1; ma è più conciso.
2.3.4.10. Ordine di precedenza generale degli operatori
() [] funzione | gd |
! ~ ++ -- | dg |
nuovi operatori di conversione (tipo) | dg |
* / % | gd |
+ - | gd |
<< >> | gd |
< <= > >= instanceof | gd |
== != | gd |
& | gd |
^ | gd |
| | gd |
&& | gd |
|| | gd |
? : | dg |
= += -= ecc. . | dg |
gd: indica che, per gli operatori di pari precedenza, viene osservata la precedenza da sinistra a destra. Ciò significa che quando un'espressione contiene operatori della stessa precedenza, viene valutato per primo l'operatore più a sinistra nell'espressione. dg indica la precedenza da destra a sinistra.
2.3.4.11. Conversione di tipo
È possibile, all'interno di un'espressione, modificare temporaneamente la rappresentazione di un valore. Questo processo è chiamato conversione di tipo. La sintassi per modificare il tipo di un valore in un'espressione è (tipo) valore. Il valore assume quindi il tipo specificato. Ciò comporta una modifica nella rappresentazione del valore.
Esempio:
Qui è necessario convertire i o j in un tipo a virgola mobile; altrimenti, la divisione restituirà un quoziente intero anziché un valore a virgola mobile.
*i* è un valore codificato esattamente in 2 byte
*(float) i* è lo stesso valore, codificato approssimativamente come un numero in virgola mobile su 4 byte
Si ha quindi una conversione di tipo del valore di i. Questa conversione avviene solo per la durata di un calcolo; la variabile i conserva sempre il suo tipo int.
2.4. Istruzioni di controllo del flusso del programma
2.4.1. Basta
Il metodo exit definito nella classe System consente di interrompere l'esecuzione di un programma.
Azione : interrompe il processo corrente e restituisce il valore di stato al processo padre
exit termina il processo corrente e restituisce il controllo al processo chiamante. Il valore di stato può essere utilizzato dal processo chiamante. In DOS, questa variabile di stato viene restituita a DOS nella variabile di sistema ERRORLEVEL, il cui valore può essere verificato in un file batch. In Unix, la variabile $? recupera il valore di stato se l'interprete di comandi è la Bourne Shell (/bin/sh).
Esempio:
per terminare il programma con un valore di stato pari a 0.
2.4.2. Struttura condizionale semplice
syntaxe : if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}
note:
- La condizione è racchiusa tra parentesi.
- Ogni azione è terminata da un punto e virgola.
- Le parentesi graffe non sono seguite da un punto e virgola.
- Le parentesi graffe sono necessarie solo se c'è più di un'azione.
- La clausola else può essere omessa.
- Non c'è "then".
L'equivalente algoritmico di questa struttura è la struttura if-then-else:
esempio
if (x>0) { nx=nx+1;sx=sx+x;} else dx=dx-x;
È possibile nidificare le strutture decisionali:
A volte si presenta il seguente problema:
public static void main(void){
int n=5;
if(n>1)
if(n>6)
System.out.println(">6");
else System.out.println("<=6");
}
Nell'esempio precedente, a quale istruzione if si riferisce l'else? La regola è che un else si riferisce sempre all'istruzione *if più vicina: if(n>6)* in questo esempio. Consideriamo un altro esempio:
public static void main(void)
{ int n=0;
if(n>1)
if(n>6) System.out.println(">6");
else; // else from if(n>6): nothing to do
else System.out.println("<=1"); // else du if(n>1)
}
Qui volevamo inserire un else nell'istruzione if(n>1) e nessun else nell'istruzione if(n>6). A causa della nota precedente, siamo costretti a inserire un else nell'istruzione if(n>6), nella quale non c'è alcuna istruzione.
2.4.3. Struttura case
La sintassi è la seguente:
switch(expression) {
case v1:
actions1;
break;
case v2:
actions2;
break;
. .. .. .. .. ..
default: actions_sinon;
}
note
- Il valore dell'espressione di controllo può essere solo un numero intero o un carattere.
- L'espressione di controllo è racchiusa tra parentesi.
- La clausola predefinita può essere omessa.
- I valori vi sono valori possibili dell'espressione. Se l'espressione restituisce il valore vi, vengono eseguite le azioni che seguono la clausola case vi.
- L'istruzione break esce dalla struttura case. Se è assente alla fine del blocco di istruzioni per il valore vi, l'esecuzione prosegue con le istruzioni per il valore vi+1.
esempio
Negli algoritmi
selon la valeur de choix
cas 0
arrêt
cas 1
exécuter module M1
cas 2
exécuter module M2
sinon
erreur<--vrai
findescas
In Java
int choix, erreur;
switch(choix){
case 0: System.exit(0);
case 1: M1();break;
case 2: M2();break;
default: erreur=1;
}
2.4.4. Struttura del ciclo
2.4.4.1. Numero noto di ripetizioni
Sintassi
for (i=id;i<=if;i=i+ip){
actions;
}
Note
- I tre argomenti del ciclo for sono racchiusi tra parentesi.
- I tre argomenti del ciclo for sono separati da punti e virgola.
- Ogni azione nel ciclo for è terminata da un punto e virgola.
- La parentesi graffa è necessaria solo se c'è più di un'azione.
- La parentesi graffa non è seguita da un punto e virgola.
L'equivalente algoritmico è la struttura for:
che può essere tradotto in una struttura while:
2.4.4.2. Numero di ripetizioni sconosciuto
In Java esistono molte strutture di controllo per questo caso.
Ciclo while
while(condition){
actions;
}
Il ciclo continua finché la condizione è vera. Il ciclo potrebbe non essere mai eseguito.
Note:
- La condizione è racchiusa tra parentesi.
- Ogni azione è terminata da un punto e virgola.
- Le parentesi graffe sono necessarie solo se c'è più di un'azione.
- La parentesi graffa non è seguita da un punto e virgola.
La struttura algoritmica corrispondente è la struttura while:
Struttura do-while
La sintassi è la seguente:
do{
instructions;
}while(condition);
Il ciclo continua finché la condizione non diventa falsa o finché la condizione rimane vera. In questo caso, il ciclo viene eseguito almeno una volta.
Note
- La condizione è racchiusa tra parentesi.
- Ogni azione è terminata da un punto e virgola.
- Le parentesi graffe sono necessarie solo se c'è più di un'azione.
- La parentesi graffa non è seguita da un punto e virgola.
La struttura algoritmica corrispondente è la struttura "repeat ... until":
Struttura per "in generale" (for)
La sintassi è la seguente:
for(instructions_départ;condition;instructions_fin_boucle){
instructions;
}
Il ciclo continua finché la condizione è vera (valutata prima di ogni iterazione). Le istruzioni_di_inizio vengono eseguite prima di entrare nel ciclo per la prima volta. Le istruzioni_di_fine_ciclo vengono eseguite dopo ogni iterazione.
Note
- I tre argomenti del ciclo for sono racchiusi tra parentesi.
- I tre argomenti del ciclo for sono separati da punti e virgola.
- Ogni istruzione for termina con un punto e virgola.
- La parentesi graffa è necessaria solo se c'è più di un'azione.
- La parentesi graffa non è seguita da un punto e virgola.
- Le varie istruzioni in start_statements e end_loop_statements sono separate da virgole.
La struttura algoritmica corrispondente è la seguente:
Esempi
I seguenti programmi calcolano tutti la somma dei primi n numeri interi.
1 for(i=1, somme=0;i<=n;i=i+1)
somme=somme+a[i];
2 for (i=1, somme=0;i<=n;somme=somme+a[i], i=i+1);
3 i=1;somme=0;
while(i<=n)
{ somme+=i; i++; }
4 i=1; somme=0;
do somme+=i++;
while (i<=n);
Istruzioni di controllo del ciclo
break | Esce dal ciclo for, while o do...while. |
continue | passa all'iterazione successiva dei cicli for, while e do...while |
2.5. La struttura di un programma Java
Un programma Java che non utilizza classi o funzioni definite dall'utente diverse dalla funzione main può avere la seguente struttura:
public class test1{
public static void main(String arg[]){
… code du programme
}// hand
}// class
La funzione main, nota anche come metodo, è la prima ad essere eseguita all'avvio di un programma Java. Deve avere la seguente firma:
public static void main(String arg[]){
oppure
public static void main(String[] arg){
Il nome dell'argomento arg può essere qualsiasi cosa. Si tratta di un array di stringhe che rappresenta gli argomenti della riga di comando. Torneremo su questo punto più avanti.
Se si utilizzano funzioni che potrebbero generare eccezioni che non si desidera gestire esplicitamente, è possibile racchiudere il codice del programma in un blocco try/catch:
public class test1{
public static void main(String arg[]){
try{
… code du programme
} catch (Exception e){
// error handling
}// try
}// hand
}// class
All'inizio del codice sorgente e prima della definizione della classe, è comune trovare istruzioni di importazione della classe. Ad esempio:
import java.io.*;
public class test1{
public static void main(String arg[]){
… code du programme
}// hand
}// class
Facciamo un esempio. Consideriamo la seguente istruzione di scrittura:
che stampa "java" sullo schermo. C'è molto da scoprire in questa semplice istruzione:
- System è una classe il cui nome completo è java.lang.System
- out è una proprietà di questa classe di tipo java.io.PrintStream, un'altra classe
- println è un metodo della classe java.io.PrintStream.
Non complicheremo inutilmente questa spiegazione, che arriva troppo presto poiché richiede la comprensione del concetto di classe che non è stato ancora trattato. Una classe può essere considerata come una risorsa. In questo caso, il compilatore avrà bisogno di accedere sia alla classe java.lang.System che a quella java.io.PrintStream. Le centinaia di classi Java sono organizzate in archivi noti anche come pacchetti. Le istruzioni import poste all'inizio del programma servono a indicare al compilatore di quali classi esterne il programma ha bisogno (quelle utilizzate ma non definite nel file sorgente da compilare). Pertanto, nel nostro esempio, il nostro programma necessita delle classi java.lang.System e java.io.PrintStream. Lo specifichiamo con l'istruzione import. Potremmo scrivere all'inizio del programma:
Poiché un programma Java utilizza comunemente decine di classi esterne, sarebbe noioso scrivere tutte le istruzioni di importazione necessarie. Le classi sono state raggruppate in pacchetti e possiamo importare l'intero pacchetto. Pertanto, per importare i pacchetti java.lang e java.io, scriviamo:
Il pacchetto java.lang contiene tutte le classi di base di Java e viene importato automaticamente dal compilatore. Quindi, in definitiva, scriveremo semplicemente:
2.6. Gestione delle eccezioni
Molte funzioni Java sono in grado di generare eccezioni, ovvero errori. Abbiamo già incontrato una funzione di questo tipo, la funzione readLine:
String ligne=null;
try{
ligne=IN.readLine();
System.out.println("ligne="+ligne);
} catch (Exception e){
affiche(e);
System.exit(2);
}// try
Quando una funzione rischia di generare un'eccezione, il compilatore Java richiede al programmatore di gestirla al fine di produrre programmi più resistenti agli errori: è necessario evitare sempre che un'applicazione si "blocchi" in modo imprevisto. In questo caso, la funzione readLine genera un'eccezione se non c'è nulla da leggere, ad esempio perché il flusso di input è stato chiuso. La gestione delle eccezioni segue questo schema:
try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Se la funzione non genera un'eccezione, il programma passa all'istruzione successiva; in caso contrario, entra nel corpo della clausola catch e poi passa all'istruzione successiva. Si notino i seguenti punti:
- e è un oggetto derivato dal tipo Exception. Possiamo essere più specifici utilizzando tipi come IOException, SecurityException, ArithmeticException, ecc.: esistono circa venti tipi di eccezioni. Scrivendo catch (Exception e), indichiamo che vogliamo gestire tutti i tipi di eccezioni. Se il codice nel blocco try è suscettibile di generare più tipi di eccezioni, potremmo voler essere più specifici gestendo l'eccezione con più blocchi catch:
try{
appel de la fonction susceptible de générer l'exception
} catch (IOException e){
traiter l'exception e
}
} catch (ArrayIndexOutOfBoundsException e){
traiter l'exception e
}
} catch (RunTimeException e){
traiter l'exception e
}
instruction suivante
- È possibile aggiungere una clausola finally ai blocchi try/catch:
try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
finally{
code exécuté après try ou catch
}
instruction suivante
In questo caso, indipendentemente dal fatto che si verifichi o meno un'eccezione, il codice nella clausola finally verrà sempre eseguito.
- La classe Exception dispone di un metodo getMessage() che restituisce un messaggio che descrive in dettaglio l'errore verificatosi. Quindi, se vogliamo visualizzare questo messaggio, scriviamo:
catch (Exception ex){
System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
...
}//catch
- La classe Exception dispone di un metodo toString() che restituisce una stringa indicante il tipo di eccezione e il valore della proprietà Message. Possiamo quindi scrivere:
catch (Exception ex){
System.err.println ("L'erreur suivante s'est produite : "+ex.toString());
...
}//catch
Possiamo anche scrivere:
Qui abbiamo un'operazione stringa + Exception che verrà automaticamente convertita in stringa + Exception.toString() dal compilatore al fine di concatenare due stringhe.
L'esempio seguente mostra un'eccezione generata dall'utilizzo di un elemento di array inesistente:
// tables
// imports
import java.io.*;
public class tab1{
public static void main(String[] args){
// declaring & initializing an array
int[] tab=new int[] {0,1,2,3};
int i;
// table display with for
for (i=0; i<tab.length; i++)
System.out.println("tab[" + i + "]=" + tab[i]);
// generating an exception
try{
tab[100]=6;
}catch (Exception e){
System.err.println("L'erreur suivante s'est produite : " + e);
}//try-catch
}//hand
}//class
L'esecuzione del programma produce i seguenti risultati:
tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
L'erreur suivante s'est produite : java.lang.ArrayIndexOutOfBoundsException
Ecco un altro esempio in cui gestiamo l'eccezione causata dall'assegnazione di una stringa a un numero quando la stringa non rappresenta un numero:
// imports
import java.io.*;
public class console1{
public static void main(String[] args){
// creation of an input stream
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
}catch(Exception ex){}
// We ask for the name
System.out.print("Nom : ");
// reading response
String nom=null;
try{
nom=IN.readLine();
}catch(Exception ex){}
// age requested
int age=0;
boolean ageOK=false;
while ( ! ageOK){
// question
System.out.print("âge : ");
// read-verify answer
try{
age=Integer.parseInt(IN.readLine());
ageOK=true;
}catch(Exception ex) {
System.err.println("Age incorrect, recommencez...");
}//try-catch
}//while
// final display
System.out.println("Vous vous appelez " + nom + " et vous avez " + age + " ans");
}//Main
}//class
Alcuni risultati dell'esecuzione:
E:\data\serge\MSNET\c#\bases\1>console1
Nom : dupont
âge : xx
Age incorrect, recommencez...
âge : 12
Vous vous appelez dupont et vous avez 12 ans
2.7. Compilazione ed esecuzione di un programma Java
Compilare ed eseguire il seguente programma:
// importing classes
import java.io.*;
// test class
public class coucou{
// hand function
public static void main(String args[]){
// screen display
System.out.println("coucou");
}//hand
}//class
Il file sorgente contenente la classe coucou precedente deve essere denominato coucou.java:
La compilazione e l'esecuzione di un programma Java avvengono in una finestra DOS. I file eseguibili javac.exe (compilatore) e java.exe (interprete) si trovano nella directory bin della directory di installazione del JDK:
E:\data\serge\JAVA\classes\paquetages\personne>dir "e:\program files\jdk14\bin\java?.exe"
07/02/2002 12:52 24 649 java.exe
07/02/2002 12:52 28 766 javac.exe
Il compilatore javac.exe analizzerà il file sorgente .java e produrrà un file .class compilato. Questo file non è immediatamente eseguibile dal processore. Richiede un interprete Java (java.exe), noto come macchina virtuale o JVM (Java Virtual Machine). Dal codice intermedio nel file .class, la macchina virtuale genera istruzioni specifiche per il processore della macchina su cui è in esecuzione. Esistono macchine virtuali Java per diversi tipi di sistemi operativi (Windows, Unix, Mac OS, ecc.). Un file .class può essere eseguito da una qualsiasi di queste macchine virtuali e quindi su qualsiasi sistema operativo. Questa portabilità tra sistemi è uno dei principali punti di forza di Java.
Compiliamo il programma precedente:
E:\data\serge\JAVA\ESSAIS\intro1>"e:\program files\jdk14\bin\javac" coucou.java
E:\data\serge\JAVA\ESSAIS\intro1>dir
10/06/2002 08:42 228 coucou.java
10/06/2002 08:48 403 coucou.class
Eseguiamo il file .class generato:
Si noti che nel comando di esecuzione sopra riportato non abbiamo specificato il suffisso .class per il file coucou.class da eseguire. È implicito. Se la directory bin del JDK si trova nel PATH del computer DOS, non è necessario fornire il percorso completo degli eseguibili javac.exe e java.exe. È sufficiente scrivere
2.8. Argomenti principali del programma
La funzione principale accetta come parametri un array di stringhe: String[]. Questo array contiene gli argomenti della riga di comando utilizzati per avviare l'applicazione. Pertanto, se avviamo il programma P con il comando:
e se la funzione principale è dichiarata come segue:
avremo arg[0]="arg0", arg[1]="arg1" … Ecco un esempio:
import java.io.*;
public class param1{
public static void main(String[] arg){
int i;
System.out.println("Nombre d'arguments="+arg.length);
for (i=0;i<arg.length;i++)
System.out.println("arg["+i+"]="+arg[i]);
}
}
I risultati sono i seguenti:
2.9. Passaggio di parametri a una funzione
Gli esempi precedenti mostravano solo programmi Java con una singola funzione, la funzione main. L'esempio seguente illustra come utilizzare le funzioni e come avviene lo scambio di informazioni tra di esse. I parametri delle funzioni vengono sempre passati per valore: ciò significa che il valore del parametro effettivo viene copiato nel parametro formale corrispondente.
import java.io.*;
public class param2{
public static void main(String[] arg){
String S="papa";
changeString(S);
System.out.println("Paramètre effectif S="+S);
int age=20;
changeInt(age);
System.out.println("Paramètre effectif age="+age);
}
private static void changeString(String S){
S="maman";
System.out.println("Paramètre formel S="+S);
}
private static void changeInt(int a){
a=30;
System.out.println("Paramètre formel a="+a);
}
}
I risultati ottenuti sono i seguenti:
I valori dei parametri effettivi "papà" e 20 sono stati copiati nei parametri formali S e a. Questi sono stati poi modificati. I parametri effettivi sono rimasti invariati. Si noti qui il tipo dei parametri effettivi:
- S è un riferimento a un oggetto, ovvero l'indirizzo di un oggetto in memoria
- age è un numero intero
2.10. Un esempio di calcolo delle imposte
Concluderemo questo capitolo con un esempio che riprenderemo più volte nel presente documento. Proponiamo di scrivere un programma per calcolare l'imposta di un contribuente. Consideriamo il caso semplificato di un contribuente che deve dichiarare solo il proprio stipendio:
- calcoliamo il numero di scaglioni fiscali per il dipendente come nbParts = nbEnfants / 2 + 1 se è celibe, e nbEnfants / 2 + 2 se è coniugato, dove nbEnfants è il numero di figli.
- Se ha almeno tre figli, riceve una quota aggiuntiva pari a metà.
- Calcoliamo il reddito imponibile R = 0,72 × S, dove S è lo stipendio annuo
- Calcoliamo il coefficiente familiare QF = R / nbParts
- Calcoliamo la tua imposta I. Considera la seguente tabella:
12.620,0 | 0 | 0 |
13.190 | 0,05 | 631 |
15.640 | 0,1 | 1.290,5 |
24.740 | 0,15 | 2.072,5 |
31.810 | 0,2 | 3.309,5 |
39.970 | 0,25 | 4.900 |
48.360 | 0,3 | 6.898,5 |
55.790 | 0,35 | 9.316,5 |
92.970 | 0,4 | 12.106 |
127.860 | 0,45 | 16.754,5 |
151.250 | 0,50 | 23.147,5 |
172.040 | 0,55 | 30.710 |
195.000 | 0,60 | 39.312 |
0 | 0,65 | 49.062 |
Ogni riga ha 3 campi. Per calcolare l'imposta I, cerchiamo la prima riga in cui QF <= campo1. Ad esempio, se QF = 23.000, troveremo la riga
L'imposta I è quindi pari a 0,15*R - 2072,5*nbParts. Se QF è tale che la condizione QF<=field1 non è mai soddisfatta, vengono utilizzati i coefficienti dell'ultima riga. Qui:
il che dà l'imposta I = 0,65*R - 49062*nbParts.
Il programma Java corrispondente è il seguente:
import java.io.*;
public class impots{
// ------------ hand
public static void main(String arg[]){
// data
// tax bracket limits
double Limites[]={12620, 13190, 15640, 24740, 31810, 39970, 48360,55790, 92970, 127860, 151250, 172040, 195000, 0};
// coefficient applied to the number of shares
double Coeffn[]={0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5,12106, 16754.5, 23147.5, 30710, 39312, 49062};
// the program
// keyboard input stream creation
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
}
catch(Exception e){
erreur("Création du flux d'entrée", e, 1);
}
// we recover marital status
boolean OK=false;
String reponse=null;
while(! OK){
try{
System.out.print("Etes-vous marié(e) (O/N) ? ");
reponse=IN.readLine();
reponse=reponse.trim().toLowerCase();
if (! reponse.equals("o") && !reponse.equals("n"))
System.out.println("Réponse incorrecte. Recommencez");
else OK=true;
} catch(Exception e){
erreur("Lecture état marital",e,2);
}
}
boolean Marie = reponse.equals("o");
// number of children
OK=false;
int NbEnfants=0;
while(! OK){
try{
System.out.print("Nombre d'enfants : ");
reponse=IN.readLine();
try{
NbEnfants=Integer.parseInt(reponse);
if(NbEnfants>=0) OK=true;
else System.err.println("Réponse incorrecte. Recommencez");
} catch(Exception e){
System.err.println("Réponse incorrecte. Recommencez");
}// try
} catch(Exception e){
erreur("Lecture état marital",e,2);
}// try
}// while
// salary
OK=false;
long Salaire=0;
while(! OK){
try{
System.out.print("Salaire annuel : ");
reponse=IN.readLine();
try{
Salaire=Long.parseLong(reponse);
if(Salaire>=0) OK=true;
else System.err.println("Réponse incorrecte. Recommencez");
} catch(Exception e){
System.err.println("Réponse incorrecte. Recommencez");
}// try
} catch(Exception e){
erreur("Lecture Salaire",e,4);
}// try
}// while
// calculating the number of shares
double NbParts;
if(Marie) NbParts=(double)NbEnfants/2+2;
else NbParts=(double)NbEnfants/2+1;
if (NbEnfants>=3) NbParts+=0.5;
// taxable income
double Revenu;
Revenu=0.72*Salaire;
// family quotient
double QF;
QF=Revenu/NbParts;
// search for tax bracket corresponding to QF
int i;
int NbTranches=Limites.length;
Limites[NbTranches-1]=QF;
i=0;
while(QF>Limites[i]) i++;
// tax
long impots=(long)(i*0.05*Revenu-Coeffn[i]*NbParts);
// the result is displayed
System.out.println("Impôt à payer : " + impots);
}// hand
// ------------ error
private static void erreur(String msg, Exception e, int exitCode){
System.err.println(msg+"("+e+")");
System.exit(exitCode);
}// error
}// class
I risultati ottenuti sono i seguenti:
C:\Serge\java\impots\1>java impots
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : 3
Salaire annuel : 200000
Impôt à payer : 16400
C:\Serge\java\impots\1>java impots
Etes-vous marié(e) (O/N) ? n
Nombre d'enfants : 2
Salaire annuel : 200000
Impôt à payer : 33388
C:\Serge\java\impots\1>java impots
Etes-vous marié(e) (O/N) ? w
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? q
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : q
Réponse incorrecte. Recommencez
Nombre d'enfants : 2
Salaire annuel : q
Réponse incorrecte. Recommencez
Salaire annuel : 1
Impôt à payer : 0