Programmazione 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 imperativi come C/C++ o 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) {
Scanner in=new Scanner(System.in);
int x,y,somma;
System.out.print("ins.x:");
x=in.nextInt();
System.out.print("ins.y:");
y=in.nextInt();
in.close();
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.
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) {
Scanner in=new Scanner(System.in);
final String MSG = "Ciao
";
String s;
s=in.next();
System.out.println(MSG+s);
in.close();
}//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.
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 es.
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 es.
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.
Operatori logici
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
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.
E' 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.