edutecnica

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 , , ... 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.
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.