Struttura di un programma in Java
La sintassi generale usata per scrivere un programma in linguaggio Java, anche soltanto in modo procedurale cioè senza l’uso di oggetti istanziati, può essere schematizzato con la seguente sintassi:
public class nomeclasse {
public static void main (String args[]) {
[istruzioni]
}//fine main
// altri eventuali metodi (funzioni o sottoprogrammi)
}// fine class
In un’applicazione console, il metodo main() deve sempre essere presente e dichiara il punto in cui inizia l’esecuzione del programma. Questa formulazione (orientata agli oggetti) rappresenta un passaggio obbligato anche se si volessero eseguire semplici programmi facilmente scrivibili in linguaggi sequenziali (anche se strutturati) come il C o il Pascal.
Prendiamo ad esempio il seguente listato Pascal
program sum;
var
x,y,somma:integer;
begin
write(‘ins. x:’);
readln(x);
write(‘ins. y:’);
readln(y);
somma:=x+y;
writeln(somma);
end.
Esegue delle semplici operazioni di I/O: accetta due interi da tastiera (x e y) e ne restituisce la somma. Riportato in Java potremo scrivere:
import java.util.Scanner;
class sum {
public static void main (String[] args) throws IOException {
int x,y,somma;
InputStreamReader input=new InputStreamReader(System.in);
BufferedReader h= new BufferedReader(input);
System.out.print(“ins.x:”);
x=Integer.parseInt(h.readLine().trim());
System.out.print(“ins.y:”);
y=Integer.parseInt(h.readLine().trim());
somma=x+y;
System.out.println(somma);
}//fine main
} //fine class
In prima battuta non si può certo dire che il linguaggio Java abbia introdotto delle semplificazioni: qualche chiarimento è obbligatorio.
import java.util.Scanner;
ha lo scopo di importare nel programma la classe Scanner utilizzata per facilitare lo stream di input.
Scanner in=new Scanner(System.in)
crea un’oggetto di classe scanner chiamato in che può disporre di metodi come nextInt() o nextLine() in grado di accettare dati in input da tastiera.
Mentre per l’output può essere usata la tradizionale istruzione System.out.println(), lo stream di input in Java può essere gestito in svariati modi, la maniera più semplice è, appunto, l’utilizzo della classe Scanner che opera su token (gettoni): un token, di default, è semplicemente una stringa priva di spazi. Il carattere spazio è infatti usato come separatore di token. In questo caso abbiamo istanziato dalla classe scanner un oggetto che abbiamo chiamato in (per brevità).
I metodi più importanto che possono essere usati per un oggetto di classe Scanner sono:
nextInt(): legge il token in ingresso interpretandolo come un numero intero.
nextDouble(): legge il token in ingresso interpretandolo come un numero reale.
nextLine(): legge la riga di testo in ingresso.
next(): legge il token in ingresso senza delimitatori (il carattere spazio per default).
close() : è usato per chiudere il flusso (stream) e rilasciare le risorse che erano occupate nello stesso.BufferedReader
Identificatori
Nella stesura del codice ci si ritrova a dover assegnare dei nomi (alle costanti, alle variabili a dei metodi) detti identificatori, questi nomi possono essere assegnati arbitrariamente, ma bisogna ricordarsi di evitare le seguenti parole riservate:
abstract | boolean | break | byte | case | catch |
char | class | const | continue | default | do |
double | else | extends | false | final | finally |
float | for | goto | if | implements | import |
inatanceof | int | interface | long | native | new |
null | package | private | protected | public | return |
short | static | super | switch | synchronized | this |
throw | throws | transient | true | try | void |
volatile | while |
Un identificatore non può dunque essere una parola riservata, deve incominciare con una lettera (a-z o A-Z) o con _ (underscore) può contenere lettere, cifre, _, a piacere
Variabili
Una variabile è un’associazione fra un identificatore e un valore; è una “scatola” che contiene un valore. In Java ogni variabile è associata ad un tipo di dati. Un tipo è quindi un insieme di valori e di operazioni eseguibili su quei valori. Prima di usare una variabile bisogna, quindi, dichiarare il suo tipo. Di solito questo viene fatto nella parte dichiarativa di un programma. La dichiarazione di una variabile si esprime con la sintassi:
Tipo nomeVariabile
sono dichiarazioni di variabili
int base, altezza; //dichiara le variabili intere base e altezza senza inizializzarle
float peri, area=3.67; //dichiara le variabili float peri e area inizializzando solo quest’ultima
char ch=’A’;//dichiarazione ed inizializzazione di variabile char
boolean a=false,b;//dichiara due boolean, inizializza solo a
int[] T;//dichiara un vettore di interi
String[] s;//dichiara un vettore di stringhe
Come in C per cambiare il contenuto di una variabile si usa un’istruzione di assegnamento:
nomeVariabile=espressione;
Lifetime delle variabili
Per quello che riguarda il tempo di vita e il campo di azione di una variabile, valgono le seguenti regole:
Una variabile locale cessa di esistere all’uscita del blocco in cui è stata dichiarata. Una variabile locale è visibile dal punto in cui viene dichiarata, fino alla fine del suo blocco.
ad esempio nel codice:
{int a=2, b=3;
{int x=a
a=b;
b=x;
}
System.out.println(a);
System.out.println(x);//dà errore :è fuori dal suo campo di azione
}
Costanti
La dichiarazione di costanti assume invece la seguente sintassi:
final int n=5;
final float prz=11.50;
final String messaggio=”ciao”;
L’uso del modificatore final è obbligatorio.
Il modificatore final, trasforma una variabile in una costante, visibile nel blocco della classe.
La tipica applicazione di test ‘ciao a tutti’ verrebbe codificata:
import java.io.*;
class ciao {
public static void main (String[] args) throws IOException {
final String MSG = “Ciao “;
InputStreamReader in=new InputStreamReader(System.in);
BufferedReader h= new BufferedReader(in);
String s;
s=h.readLine();
System.out.println(MSG+s);
}//fine main
}//fine class
Come è possibile riconoscere, Java mantiene la stessa sintassi del linguaggio C; in particolare è obbligatorio rispettare la regola del punto e virgola: cioè, ogni istruzione deve essere seguita (terminata) da un punto e virgola. Come nel caso del C, il linguaggio è case sensitive, ciò vuol dire che la variabile Pippo è considerata differente da una variabile dichiarata pippo.
Tipi di dati
I tipi di dati in Java possono essere classificati in due categorie principali:
● Tipi primitivi (basic type).
● Tipi riferimento (reference type).
come si vede rappresentato nel seguente schema:
I tipi di dati primitivi sono dunque:
boolean | 1bit | false/true |
byte | 1byte | da -128 a 127 |
short | 2byte | da -32768 a 32767 |
int | 4byte | da -231 a 231-1 |
long | 8byte | da -263 a 263-1 |
char | 2byte | da \u0000 (0) a \uFFFF (65535) |
float | 4byte | ±3.40282347·10+38 a ±1.40239846·10-45 |
double | 8byte | da±1.7976931348623157·10+308 a ±4.94065645841246544·10-324 |
A questo proposito bisogna specificare che sequenze di cifre decimali eventualmente precedute dai segni + o – anche eventualmente seguite da l o L (per i long) come:
long x = 100L;
int y = 4000
sono chiamati letterali di tipo intero; in altri termini
25 è un letterale di tipo int
“pippo“ è un letterale di tipo String
I letterali sono sequenze di caratteri che rappresentano valori di tipi primitivi e stringhe.
Come si vede, le stringhe fanno parte degli oggetti e non dei tipi di dati primitivi.
I tipi riferimento a differenza dei tipi primitivi sono “aperti” al programmatore che ne può introdurre a piacere.
Quando un programmatore introduce una classe istanziandone degli oggetti è come se introducesse un nuovo tipo di dato in questa gerarchia.Nonostante non vi sia il tipo primitivo string (ma solo la classe String) Java permette di avere letterali stringhe, che vengono racchiusi fra doppi apici, come “Ciao ” già visto nel programma precedente.
Espressioni
Un’espressione è una porzione di codice Java, che ha:
● un tipo determinato al momento della compilazione
● un valore determinato al momento dell’esecuzione
i + j
Se i e j sono di tipo int è un’espressione int
“ciao”.toUpperCase()
ha tipo String (il suo risultato è un riferimento a un oggetto di tipo String) .
Le espressioni composte sono costituite combinando espressioni di base mediante operatori aritmetici (+,-.*,/), relazionali (==,!=,≤,≥,…etc.) o logici (&&, ||, !).
Variabili, letterali e costanti possono essere combinate, usando opportuni operatori e rispettando alcune regole, per formare espressioni analoghe alle espressioni algebriche. Gli operatori hanno differenti priorità, o precedenze, secondo le normali consuetudini dell’algebra ad es. in a*b-c la moltiplicazione viene effettuata prima dell’addizione. Le parentesi permettono di cambiare l’ordine di esecuzione delle operazioni. Dobbiamo come al solito ricordare che se in un’espressione sono coinvolti tipi di dati differenti, il risultato sarà dello stesso tipo del dato che che occupa maggior spazio in memoria. Ad esempio:
int a=3,c=7;
double b=2.50;
System.out.println(a+b);
System.out.println(c/a);
fornisce in uscita
5.5
2
questo perché in a+b il dato di maggior occupazione in memoria è double, il risultato in tal caso è di tipo double; mentre in c/a il dato di maggior occupazione in memoria è sempre int, il risultato in tal caso è di tipo int. Si raccomanda in tal caso la conversione di cast :
System.out.println((double)c/a);
per evitare la perdita dei decimali.
Operatori aritmetici
Come dice il nome gli operatori aritmetici servono per eseguire operazioni aritmetiche.
operatore | azione |
– | sottrazione |
+ | addizione |
* | moltiplicazione |
/ | divisione |
% | resto della divisione fra interi |
— | decremento |
++ | incremento |
Gli operatori aritmetici vengono spesso usati per abbreviare l’assegnamento di variabili
x += y → x = x + y
x -= y → x = x – y
x *= y → x = x * y
x /= y → x = x / y
Operatori di incremento (++) e decremento (—) sono operatori unari e si applicano a variabili di tipo numerico (int,. . ,double,. .) si possono utilizzare prefissi o postfissi (cambia la semantica) .
Notazione prefissa
i = 1;
j = ++i; // i = j = 2
prima viene incrementata la variabile, poi viene valutata l’espressione.
Notazione postfissa
i = 1;
j = i++; // i = 2, j = 1
prima viene valuta l’espressione, poi viene incrementata la variabile come si vede anche dal seguente programma.
class test {
public static void main(String[] args) {
int i=5;
System.out.print(i=i++);//_> 5
System.out.print(i++);//___> 5
System.out.print(i);//_____> 6
}//fine__main
}//fine class
Operatori relazionali
Gli operatori relazionali, intervengono nelle espressioni logiche che implicano, prevalentemente, dati di tipo numerico.
Ovviamente restituiscono un dato booleano (true/false). Ad esempio:
class operatori {
public static void main (String[] args) {
int x,y;
boolean p,q;
x=10;
y=10;
p=(x==y);
q=(x!=y);
System.out.println(“p:”+ p+”\t” +”q:”+q);
}//fine main
}//fine class
restituisce:
p:true q:false
dato che la condizione logica p è vera, q è falsa.
Gli operatori relazionali sono i seguenti:
operatore | azione |
> | maggiore |
>= | maggiore o uguale |
< | minore |
<= | minore o uguale |
== | uguale a |
!= | diverso da |
Uno degli errori più ricorrenti è quello di usare l’operatore di assegnamento ‘=’ al posto di ‘==’ nelle espressioni condizionali.
Gli operatori logici sono coinvolti nelle associazioni fra le relazioni:
operatore | azione |
&& | and |
|| | or |
! | not |
Ad esempio:
class logici {
public static void main (String[] args) {
int x=12;
boolean dueCifre=((x>=10)&&(x<=100));
boolean multiploDi6=((x%6)==0);
boolean dispari=!((x%6)==0);
System.out.println(“dueCifre:”+dueCifre);
System.out.println(“multiploDi6:”+multiploDi6);
System.out.println(“dispari:”+dispari);
}//fine main
}//fine class
restituisce
dueCifre:true
multiploDi6:true
dispari:false
come ci si poteva aspettare.
Funzioni matematiche
Ricordiamo che l’eventuale uso delle funzioni matematiche, implica la restituzione da parte di alcune di queste di numeri in virgola mobile:
operatore | azione |
E | costante numero di Neper=2.717.. |
PI | costante pi greco=3.14.. |
abs(x) | valore assoluto di x |
max(x,y) | massimo fra due numeri |
min(x,y) | minimo fra due numeri |
sin(x),cos(x),tan(x) | funzioni trigonometriche |
asin(x),acos(x),atan(x) | funzioni trigonometriche inverse |
ceil(x) | il più piccolo intero maggiore o uguale a x |
floor(x) | il più grande intero minore o uguale a x |
rint(x) | arrotondamento di un double verso un intero |
round(x) | arrotondamento di un float verso un intero |
exp(x) | e elevato alla x |
pow(x,y) | x elevato alla y |
log(x) | logaritmo in base e di x |
sqrt(x) | radice quadrata di x |
toDegrees(x) | conversione da radianti a gradi |
toRadians | conversione da gradi a radianti |
random() | genera un random di tipo double tra 0.0 e 1.0 |
un esempio del loro uso:
import java.lang.Math;
class potenza {
public static void main (String[] args) {
int x=4,y=2;
double z;
z=Math.pow(x,y);
System.out.println(z);
}//fine main
}//fine class
Strutture di controllo
Le strutture di controllo, specificano l’ordine di esecuzione delle istruzioni di un programma. Così come per gli operatori le strutture di controllo Java possono essere considerate identiche a quelle già viste in linguaggio C.
if-else
L’if-else è la più comune istruzione di selezione. La sua sintassi può essere espressa nel modo seguente,
![]() |
if(condizione){ istruzioni-a; } Nel caso che la condizione logica sia falsa, non viene intrapresa alcuna azione e il flusso del programma riprende dall’istruzione successiva all’if. Le parentesi graffe sono obbligatorie se deve essere eseguita più di una istruzione (blocco di istruzioni) |
![]() |
if(condizione){ istruzioni-a; } else{ istruzioni-b; } Quindi, un’istruzione di selezione inizia con la parola chiave if, seguita da un’espressione logica (condizione) fra parentesi tonde (detta condizione dell’if), seguita a sua volta da un blocco di istruzioni e, opzionalmente dalla parola chiave else e da un’altro blocco di istruzioni. |
Un esempio di uso dell’if è:
if (x >= 0) System.out.println(“x e’ positivo”);
Se la condizione dell’ if è valutata a true viene eseguita l’istruzione successiva . Se è presente anche l’else:
if (x >= 0) System.out.príntln(“x e’ positivo”);
else System.out.println(“x e’ negativo”);
l’istruzione dopo l’else viene eseguita se la condizione vale false. Si dice che l’istruzione che appare subito dopo la condizione è il ramo if, mentre quella che appare dopo l’else è il ramo else.
Operatore condizionale “?”
Operatore condizionale può essere utilizzato al posto dell’istruzione if-else con la seguente sintassi:
var (condizione) ? espressione1 : espressione2;
● L’operatore condizionale ? è’ un operatore ternario.
● condizione è un’espressione booleana.
● espressione1 e espressione2 sono espressioni dello stesso tipo (o di tipi compatibili).
● var è una variabile adatta a contenere il tipo di dato restituito dalle espressioni.
Il tipo di dato restituito da questa istruzione è lo stesso di espressione1 e espressione2.
Il valore restituito dall’istruzione è quello di espressione1 se condizione vera (true) è invece il valore di espressione2 se condizione è valutata false.
Un esempio di uso è il seguente listato:
class ot {
public static void main(String[] args) {
int x=12;
String st;
st=(x>10) ? “alto”:”basso”;
System.out.println(st);
}//fine main
} //fine class
In questo caso il programma risponderà con la stringa “alto” se il valore di x>10 altrimenti risponderà restituendo la stringa “basso” assegnata alla variabile st.
If-else-if
Come già visto in linguaggio C, rimane valido anche il costrutto if-else-if qui esemplificato in un programma che determina il maggiore di 4 caratteri:
import java.util.Scanner;
class ifElseIf {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int a, b, c, d, max;
a = in.nextInt();
b = in.nextInt();
c = in.nextInt();
d = in.nextInt();
if (a>b && a>c && a>d) max = a;
else if (b>a && b>c && b>d) max = b;
else if (c>a && c>b && c>d) max = c;
else max = d;
System.out.println(max);
in.close();
}//fine main
} //fine class
Switch
Lo stesso schema di flusso dell’istruzione if-else-if viene seguita dal costrutto switch
switch (espressione) {
case valore_a: {istruzioni_a;} break;
case valore_b: {istruzioni_b;} break;
…
case valore_n: {istruzioni_n;} break;
default : {istruzioni_n;} break;
} //fine switch
L’espressione che segue la parola chiave switch (che può anche essere una semplice variabile) viene valutata e il valore ottenuto (che deve essere di tipo byte, short, int , long o char) è confrontato con tutti i letterali , … che seguono la parola chiave case. Se sono uguali, viene eseguita la sequenza di istruzioni corrispondente; se dopo tali istruzioni vi è la parola chiave break, il controllo passa all’istruzione successiva allo switch. Se nessun letterale è uguale al valore dell’espressione viene eseguita la sequenza di istruzioni che segue la parola chiave default .
while
Capita spesso di voler ripetere più volte le stesse istruzioni. Nei primi linguaggi di programmazione vi erano le istruzioni di salto (goto, jump); nei linguaggi di programmazione moderni le istruzioni di salto sono state sostituite dalle strutture di controllo iterative. Un ciclo while (detto anche ciclo con ripetizione precondizionale) avviene nel modo seguente: la condizione viene valutata e, se il suo valore è true, il corpo viene eseguito; poi la condizione viene valutata nuovamente e il corpo eventualmente eseguito di nuovo, e così via, finché la valutazione della condizione dà il risultato false; a questo punto viene eseguita l’istruzione successiva.
![]() |
il seguente programma utilizza un ciclo while per generare casualmente un numero compreso fra 1 e 10 (inclusi), per poi stampare a video il numero. Il ciclo continuerà a funzionare finchè la condizione logica del while è vera (true). Quando viene generato un numero maggiore o uguale a 8 la condizione diventa falsa ed il ciclo si interrompe. |
Il ciclo viene eseguito almeno una volta se la variabile testata nella condizione logica è minore di 8, altrimenti, non viene eseguito nemmeno una volta.
class cicloWhile {
public static void main (String[] args) {
int x=0;
while(x<8){
x=(1 + (int) (Math.random() * 10));
System.out.println(x); }
}//fine main
}//fine class
do-while
Detto anche ciclo con ripetizione condizionale :
do{
istruzioni;
}while(condizione);
![]() |
la sequenza di istruzioni compresa tra il do e il while viene ripetuta tante volte mentre la condizione scritta dopo il while si mantiene vera; in altre parole la ripetizione termina quando la condizione diventa falsa. A differenza dei cicli for e while che verificano la condizione all’inizio del ciclo, il do-while la verifica alla fine, con la conseguenza che il do-while viene eseguito almeno sempre almeno una volta. |
class cicloDoWhile{
public static void main (String[] args) {
int x;
do{
//genera un numero casuale fra 1 e 10 compresi
x=(1 + (int) (Math.random() * 10));
System.out.println(x);
}while(x<8);
}//fine main
}//fine class
in questo caso la variabile può anche non essere inizializzata perché entra nel ciclo almeno una volta e lì viene impostata.
Break e continue
Le due istruzioni break e continue possono svolgere un importante ruolo di controllo durante l’esecuzione di un ciclo iterativo.
break termina l’esecuzione del blocco dell’iterazione in cui compare.
continue provoca l’interruzione dell’esecuzione del blocco di istruzioni interne al ciclo e il passaggio all’iterazione successiva.
Nel seguente programma le due istruzioni sono usate opportunamente per permettere l’esecuzione di una serie di numeri interi inseritida tastiera.
La seriazione viene terminata quando si inserisce il numero 0.
import java.util.Scanner;
class breakcontinue{
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int x,s=0;
do{ x=in.nextInt();
if(x==0)break;
if(x%2==1)continue;
s+=x;
}while(true);
System.out.println(s);
in.close();
}//fine__main
}//fine class
for
Il costrutto for viene usato per eseguire un numero finito di cicli, la sua sintassi è:
for (inizializzazione; condizione;incremento){
istruzioni
}
![]() |
Dopo la parola chiave for, vi sono tre elementi fra parentesi tonde (detti, da sinistra a destra: l’inizializzazione, la condizione di uscita e l’istruzione di incremento) che formano l’intestazione del for e un’istruzione (detta corpo del for), che può in generale essere composta (in tal caso è obbligatorio usare le parentesi graffe). Il significato dell’istruzione for è il seguente: l’istruzione di inizializzazione assegna un valore iniziale a una variabile, detta variabile di controllo; il corpo del for viene eseguito ripetutamente fìnché vale la condizione di uscita; e a ogni iterazione la variabile di controllo viene incrementata come indicato dall’istruzione di incremento. Ad esempio, la seguente istruzione stampa tutti i numeri da 1 a 10: |
int i;
for(i=1;i<=10; i++) System.out.println(i);
Come si nota in questo caso, non vengono usate le parentesi graffe, dato che ad ogni ciclo viene usata una sola istruzione, la variabile di controllo è la i, dichiarata preventivamente.
L’espressione di inizializzazione può contenere direttamente la dichiarazione della variabile di controllo come si vede:
for(int i=1;i<=10; i++) System.out.println(i);
in questo caso la variabile i non è definita al di fuori del ciclo, e un suo uso al di fuori dallo stesso, genera un errore di compilazione.
È possibile anche utilizzare più variabili di controllo le cui istruzioni di inizializzazione e di incremento/decremento devono essere separati dal delimitatore “,” (virgola).
class palindroma {
public static void main(String[] args) {
String s=”ANNA”;
boolean pal=true;
for (int sx=0,dx=s.length()-1;sx<dx;sx++,dx–)
if (s.charAt(sx) != s.charAt(dx)) {
pal = false;
break;
}
System.out.println(pal);
}//fine__main
}//fine class
In questo caso si mostra come l’istruzione break possa essere usata anche in questo tipo di ciclo.