Introduzione a Java¶
Parte 2 · Linguaggi di programmazione · Capitolo 8 · Dispensa, volume 2 (PDF) · Esercizi: Introduzione a Java
Introduzione¶
Nei capitoli «Introduzione a C++» e «Introduzione a Python» abbiamo tradotto lo pseudocodice dei nostri algoritmi in un linguaggio di programmazione compilato e in uno interpretato. In questo capitolo introduciamo un terzo linguaggio di programmazione, Java, che sta a metà strada: i suoi programmi sono compilati in un codice intermedio, che viene poi eseguito da una macchina virtuale. La sua sintassi è molto vicina a quella di C++, ma ogni programma è organizzato in classi.
Il capitolo segue lo stesso percorso dei due capitoli precedenti, in modo che i tre linguaggi possano essere confrontati sezione per sezione:
-
le caratteristiche principali del linguaggio e la struttura di un programma;
-
variabili, tipi di dato, ingresso e uscita;
-
operatori, istruzioni condizionali e cicli;
-
gli array, e le liste dinamiche della classe
ArrayList; -
i metodi (il nome che
Javadà alle funzioni), i loro parametri e i loro valori di ritorno; -
un algoritmo completo, la ricerca del valore massimo in un array, scritto in pseudocodice e in
Java; -
gli operatori bit a bit e una tabella riassuntiva che confronta
JavaconC++ePython.
Ogni frammento di codice del capitolo è stato compilato con il compilatore javac ed eseguito con la macchina virtuale java (OpenJDK 25): gli output riportati negli esempi sono quelli stampati dai programmi.
Nello pseudocodice le componenti di un array \(a[1..n]\) sono indicizzate da \(1\) a \(n\); in Java, come in C++ e in Python, gli elementi di un array di dimensione \(n\) sono indicizzati da \(0\) a \(n-1\). La componente \(a[i]\) dello pseudocodice corrisponde all'elemento a[i-1] del codice Java.
8.1 Caratteristiche principali del linguaggio di programmazione¶
Le caratteristiche principali di Java sono le seguenti.
-
Javaè un linguaggio compilato e interpretato: il codice sorgente (file.java) viene prima compilato in bytecode (file.class) dal compilatoreJavajavac, e poi il bytecode viene eseguito dalla Java Virtual Machine (JVM). Questo rende i programmiJavaindipendenti dalla piattaforma: lo stesso bytecode funziona su qualunque macchina su cui sia installata una JVM («scrivi una volta, esegui ovunque»). -
Javaè un linguaggio fortemente e staticamente tipizzato: ogni variabile deve essere dichiarata con il suo tipo di dato prima dell'uso, e il suo tipo di dato non può cambiare durante l'esecuzione. -
Javaè orientato agli oggetti: ogni programma è organizzato in classi. Il punto di ingresso di ogni programmaJavaè il metodomaindi una classe. -
Javasupporta i metodi (il termineJavaper le funzioni), che permettono un approccio modulare e riutilizzabile alla programmazione. -
Come in
C++, la struttura a blocchi del codice è definita dalle parentesi graffe{}, e ogni istruzione deve terminare con il simbolo riservato “;”. -
Come in
C++, un commento su una sola riga inizia con il simbolo riservato “//”, e i commenti su più righe sono racchiusi fra i simboli riservati “/*” e “*/”.
Esempio 8.1: commenti in Java
Questo è un esempio di commento su una sola riga:
Questo è un esempio di commento su più righe:
8.1.1 La struttura di un programma¶
Prima di descrivere le singole istruzioni, osserviamo lo scheletro che ogni programma ha in comune. Un programma Java è costituito da una o più classi; l'esecuzione parte dal metodo public static void main(String[] args) della classe che viene lanciata.
Esempio 8.2: lo scheletro di un programma
public class HelloWorld {
public static void main(String[] args) {
// Qui si scrivono le istruzioni del programma
System.out.println("Ciao, mondo!");
}
}
Una classe dichiarata public deve essere salvata in un file con lo stesso nome della classe: qui HelloWorld.java. Il programma si compila e si esegue con i comandi
Il primo comando produce il file di bytecode HelloWorld.class; il secondo lo esegue (si noti che l'estensione non si scrive). L'output è
Negli esempi seguenti scriviamo solo le istruzioni che contano: a meno che non contengano un programma completo, i frammenti di codice vanno inseriti fra le parentesi graffe di main.
Ogni istruzione Java appartiene a un metodo, e ogni metodo appartiene a una classe. Ogni istruzione termina con “;”.
8.2 Variabili e tipi di dato¶
Il codice sorgente opera su variabili, e ogni variabile ha un tipo di dato.
-
I quattro tipi di dato fondamentali di
Javasono:-
Il tipo di dato “
int” rappresenta un intero memorizzato con \(32\) bit: un numero intero da \(-2^{31} = -2147483648\) a \(2^{31}-1 = 2147483647\). Per interi più grandi si usa il tipo di datolong(\(64\) bit). -
Il tipo di dato “
boolean” rappresenta un valore booleano:trueofalse(minuscolo). -
Il tipo di dato “
double” rappresenta un numero in virgola mobile a precisione doppia (\(64\) bit), come inC++. Il tipo di datofloatha precisione singola, e i suoi valori si scrivono con il suffissof(ad esempio3.14f). -
Il tipo di dato “
char” rappresenta un singolo carattere, racchiuso fra apici singoli (ad esempio'A','7').
-
-
Il tipo di dato “
String” (con laSmaiuscola) rappresenta una sequenza di caratteri racchiusa fra virgolette doppie, ed è il modo standard di trattare il testo inJava. -
Una variabile funziona come un contenitore che memorizza un valore di un tipo di dato specifico per un uso successivo.
-
Alle variabili si assegnano valori con l'operatore di assegnamento “
=”. Quando si assegna un nuovo valore, il valore precedente viene sovrascritto. -
Il compilatore controlla i tipi di dato: assegnare un valore di tipo sbagliato (per esempio la stringa
"cinque"a una variabile di tipoint) o usare una variabile che non ha ricevuto un valore è un errore di compilazione, e il programma non viene prodotto.
Le variabili devono essere dichiarate con il loro tipo di dato prima dell'uso, come in C++.
Esempio 8.3: dichiarazioni e assegnamenti di variabili
int age = 25; // Variabile intera
boolean isRaining = false; // Variabile booleana
double pi = 3.14159; // Virgola mobile a precisione doppia
float small = 3.14f; // Virgola mobile a precisione singola
double large = 123456.789; // Virgola mobile a precisione doppia
char letter = 'A'; // Variabile carattere
String name = "Alice"; // Variabile stringa
age = 26; // Il valore precedente 25 viene sovrascritto
System.out.println(age + " " + isRaining + " " + pi + " " + small);
System.out.println(large + " " + letter + " " + name);
Output:
A differenza di C++, il valore booleano è visualizzato come false e il numero 123456.789 è visualizzato con tutte le sue cifre.
Esempio 8.4: i limiti del tipo di dato int
int big = Integer.MAX_VALUE; // Il più grande int
System.out.println(big);
System.out.println(big + 1); // Overflow: il risultato non entra in 32 bit
long bigger = (long) big + 1; // Calcolato con 64 bit
System.out.println(bigger);
Output:
Quando il risultato di un'operazione fra valori int non entra in \(32\) bit, «riparte» dall'altro estremo dell'intervallo, senza alcun messaggio di errore. Gli interi di Python non vanno mai in overflow.
8.3 Ingresso e uscita¶
-
L'oggetto
System.out(l'uscita standard, fornita dalla classeSystem) ha i metodi che visualizzano valori sullo schermo. I più comuni sono:-
System.out.println(): stampa un valore seguito da un a capo; -
System.out.print(): stampa un valore senza l'a capo.
-
-
Valori e stringhe sono concatenati con l'operatore
+dentro un'istruzione di stampa: quando uno dei due operandi di+è unaString, l'altro viene convertito in testo. L'espressione è valutata da sinistra a destra, quindi un'operazione aritmetica dentro una concatenazione deve essere racchiusa fra parentesi. -
Il metodo
System.out.printf()stampa un testo formattato: nella prima stringa,%dè sostituito da un intero,%sda una stringa,%.kfda un numero in virgola mobile con esattamente \(k\) cifre dopo la virgola decimale, e%nda un a capo.
Esempio 8.5: stampe sullo schermo
int n = 42;
System.out.println("Il valore della variabile è " + n);
System.out.println("n = " + n + ", doppio = " + (n * 2));
System.out.println("n + 1 = " + n + 1); // Concatenazione: "42" poi "1"
System.out.println("n + 1 = " + (n + 1)); // Addizione, poi concatenazione
System.out.print("Niente a capo ");
System.out.println("dopo print");
System.out.printf("%d diviso %d è %.2f%n", 10, 3, 10.0 / 3);
Output:
-
Il separatore decimale stampato da
printfdipende dalle impostazioni della lingua del calcolatore: su un calcolatore impostato in italiano l'ultima riga è10 diviso 3 è 3,33, con la virgola. Il metodoprintlnusa sempre il punto. -
Per leggere l'ingresso da tastiera si usa la classe
Scannerdel pacchettojava.util. Va importata con l'istruzioneimport java.util.Scanner;, scritta all'inizio del file, prima della classe. I metodinextLine(),nextInt()enextDouble()leggono una riga di testo, un intero e un numero in virgola mobile.
Esempio 8.6: ingresso da tastiera con Scanner
import java.util.Scanner;
public class InputExample {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Inserisci il tuo nome: ");
String name = sc.nextLine(); // Legge una String
System.out.print("Inserisci un intero: ");
int n = sc.nextInt(); // Legge un int
System.out.print("Inserisci un numero reale: ");
double x = sc.nextDouble(); // Legge un double
System.out.println("Ciao " + name + ", n = " + n + ", x = " + x);
sc.close();
}
}
Esempio di esecuzione (l'utente scrive Ada, 7 e 2.5):
Inserisci il tuo nome: Ada
Inserisci un intero: 7
Inserisci un numero reale: 2.5
Ciao Ada, n = 7, x = 2.5
Come printf, il metodo nextDouble() segue le impostazioni della lingua: su un calcolatore impostato in italiano il numero reale va scritto come 2,5.
8.4 Operatori¶
Gli operatori sono simboli che eseguono operazioni su variabili e valori. Gli operatori di Java sono gli stessi di C++: operatori aritmetici, di assegnamento, di incremento e decremento, relazionali e logici.
8.4.1 Operatori aritmetici¶
Operatori aritmetici, usati per eseguire le operazioni matematiche di base:
-
+(Addizione) \(\rightsquigarrow\) Somma due valori. -
-(Sottrazione) \(\rightsquigarrow\) Sottrae un valore da un altro. -
*(Moltiplicazione) \(\rightsquigarrow\) Moltiplica due valori. -
/(Divisione) \(\rightsquigarrow\) Divide un valore per un altro. Divisione intera quando entrambi gli operandi sono interi. -
%(Resto) \(\rightsquigarrow\) Restituisce il resto di una divisione.
Esempio 8.7: operatori aritmetici
int a = 10, b = 3;
System.out.println("Addizione: " + (a + b));
System.out.println("Sottrazione: " + (a - b));
System.out.println("Moltiplicazione: " + (a * b));
System.out.println("Divisione: " + (a / b)); // Divisione intera
System.out.println("Resto: " + (a % b)); // Resto della divisione
System.out.println("Divisione reale: " + ((double) a / b));
Output:
-
Come in
C++, l'operatore di divisione/esegue la divisione intera quando entrambi gli operandi sono interi, e il quoziente è troncato verso lo zero:10 / 3vale3e-7 / 2vale-3(con resto-7 % 2uguale a-1). Per ottenere un risultato in virgola mobile almeno un operando deve essere di tipodouble, per esempio con la conversione di tipo (cast)(double) a / b. -
La conversione
(int)elimina la parte decimale di undouble:(int) 3.9vale3. Non esiste un operatore per la potenza: il metodoMath.pow(x, y)restituisce \(x^y\) comedouble.
Esempio 8.8: divisione intera e in virgola mobile
int a = 10, b = 3;
System.out.println(a / b); // Divisione intera
System.out.println(10.0 / 3); // Divisione in virgola mobile
System.out.println((double) a / b); // Conversione di a in double
System.out.printf("%.2f%n", (double) a / b); // Due cifre decimali
System.out.println(-7 / 2 + " " + -7 % 2); // Troncamento verso zero
System.out.println((int) 3.9 + " " + Math.pow(2, 10));
Output:
8.4.2 Operatori di assegnamento¶
Operatori di assegnamento, usati per assegnare valori alle variabili:
-
=(Assegnamento) \(\rightsquigarrow\) Assegna un valore a una variabile. -
+=(Assegnamento con addizione) \(\rightsquigarrow\) Somma un valore e assegna il risultato. -
-=(Assegnamento con sottrazione) \(\rightsquigarrow\) Sottrae un valore e assegna il risultato. -
*=(Assegnamento con moltiplicazione) \(\rightsquigarrow\) Moltiplica e assegna il risultato. -
/=(Assegnamento con divisione) \(\rightsquigarrow\) Divide e assegna il risultato. -
%=(Assegnamento con resto) \(\rightsquigarrow\) Calcola il resto e assegna il risultato.
Esempio 8.9: operatori di assegnamento
int x = 10; // Assegnamento
System.out.println("Valore iniziale: " + x);
x += 5; System.out.println("Dopo += 5: " + x); // x = x + 5
x -= 3; System.out.println("Dopo -= 3: " + x); // x = x - 3
x *= 2; System.out.println("Dopo *= 2: " + x); // x = x * 2
x /= 4; System.out.println("Dopo /= 4: " + x); // x = x / 4
x %= 4; System.out.println("Dopo %= 4: " + x); // x = x % 4
Output:
Due istruzioni possono essere scritte sulla stessa riga, perché ciascuna termina con “;”.
8.4.3 Operatori di incremento e decremento¶
Operatori di incremento e decremento:
-
++(Incremento) \(\rightsquigarrow\) Aumenta di 1 il valore di una variabile. -
- -(Decremento) \(\rightsquigarrow\) Diminuisce di 1 il valore di una variabile.
- Come in
C++, il pre-incremento (++x) incrementa il valore prima di restituirlo, e il post-incremento (x++) restituisce prima il valore, poi lo incrementa. La stessa logica vale per l'operatore di decremento (- -xex- -).
Esempio 8.10: operatori di incremento e decremento
int x = 5;
System.out.println("Valore iniziale: " + x);
System.out.println("Pre-incremento (++x): " + ++x);
System.out.println("Post-incremento (x++): " + x++);
System.out.println("Valore dopo il post-incremento: " + x);
System.out.println("Pre-decremento (--x): " + --x);
System.out.println("Post-decremento (x--): " + x--);
System.out.println("Valore dopo il post-decremento: " + x);
Output:
8.4.4 Operatori relazionali¶
Operatori relazionali, usati per confrontare valori e restituire un risultato boolean:
-
==(Uguale a) \(\rightsquigarrow\) Verifica se due valori sono uguali. -
!=(Diverso da) \(\rightsquigarrow\) Verifica se due valori sono diversi. -
<(Minore di) \(\rightsquigarrow\) Verifica se un valore è minore di un altro. -
>(Maggiore di) \(\rightsquigarrow\) Verifica se un valore è maggiore di un altro. -
<=(Minore o uguale a) \(\rightsquigarrow\) Verifica se un valore è minore o uguale a un altro. -
>=(Maggiore o uguale a) \(\rightsquigarrow\) Verifica se un valore è maggiore o uguale a un altro.
-
Gli operatori relazionali restituiscono un valore di tipo
boolean, visualizzato cometrueofalse(non come1e0come inC++). Questi operatori sono usati comunemente nelle istruzioni condizionali e nei cicli. -
L'operatore
==confronta i valori dei tipi di dato primitivi (int,double,char,boolean); due valoriStringsi confrontano con il metodoequals, per esempioname.equals("Alice").
Esempio 8.11: operatori relazionali
int a = 10, b = 5;
System.out.println("a == b: " + (a == b)); // Uguale a
System.out.println("a != b: " + (a != b)); // Diverso da
System.out.println("a < b: " + (a < b)); // Minore di
System.out.println("a > b: " + (a > b)); // Maggiore di
System.out.println("a <= b: " + (a <= b)); // Minore o uguale a
System.out.println("a >= b: " + (a >= b)); // Maggiore o uguale a
Output:
8.4.5 Operatori logici¶
Operatori logici, usati per le operazioni logiche, spesso nelle istruzioni condizionali:
-
&&(AND logico) \(\rightsquigarrow\) Restituiscetruese entrambe le condizioni sonotrue. -
||(OR logico) \(\rightsquigarrow\) Restituiscetruese almeno una condizione ètrue. -
!(NOT logico) \(\rightsquigarrow\) Inverte il valore booleano.
Esempio 8.12: operatori logici
boolean c1 = true, c2 = false;
System.out.println("c1: " + c1);
System.out.println("c2: " + c2);
System.out.println("c1 && c2: " + (c1 && c2)); // true solo se entrambe sono true
System.out.println("c1 || c2: " + (c1 || c2)); // true se almeno una è true
System.out.println("!c1: " + (!c1)); // Inverte il valore
System.out.println("!c2: " + (!c2));
Output:
8.5 Istruzioni condizionali¶
Gli operatori relazionali e logici producono le condizioni che decidono quali istruzioni vengono eseguite.
-
L'istruzione
ifvaluta una condizione racchiusa fra parentesi(). Se la condizione ètrue, viene eseguito il blocco racchiuso fra parentesi graffe{}. La clausola opzionaleelse ifverifica condizioni aggiuntive, e la clausola opzionaleelsetratta tutti i casi rimanenti. -
A differenza di
C++, la condizione deve essere di tipoboolean: un intero non è accettato come condizione, quindiif (n % 2)è un errore di compilazione e il test va scrittoif (n % 2 != 0).
Esempio 8.13: istruzione condizionale
int number = 10;
// Verifica se il numero è positivo
if (number > 0) {
System.out.println("Il numero è positivo.");
} else {
System.out.println("Il numero non è positivo.");
}
Output:
Esempio 8.14: if / else if / else
int score = 85;
String grade;
if (score >= 90) { grade = "A"; }
else if (score >= 80) { grade = "B"; }
else if (score >= 70) { grade = "C"; }
else if (score >= 60) { grade = "D"; }
else { grade = "F"; }
System.out.println("Punteggio: " + score + ", Voto: " + grade);
Output:
Le condizioni sono valutate dall'alto verso il basso, e viene eseguito solo il blocco della prima condizione vera. La variabile grade è dichiarata senza un valore: il compilatore la accetta perché ogni ramo le assegna un valore prima che venga stampata.
8.5.1 Operatore ternario (operatore condizionale)¶
- L'operatore ternario
?:è lo stesso diC++. Sintassi:condizione ? valore_se_vero : valore_se_falso.
Esempio 8.15: operatore ternario
int number = 10;
// condizione ? valore_se_vero : valore_se_falso
String result = (number > 0) ? "positivo" : "non positivo";
System.out.println("Il numero è " + result);
// Ricerca del massimo fra due numeri
int a = 15, b = 20;
int maximum = (a > b) ? a : b;
System.out.println("Massimo: " + maximum);
// Verifica se pari o dispari
int n = 7;
String parity = (n % 2 == 0) ? "pari" : "dispari";
System.out.println(n + " è " + parity);
// Valore assoluto
int x = -5;
int absValue = (x >= 0) ? x : -x;
System.out.println("Il valore assoluto di " + x + " è " + absValue);
Output:
La classe Math fornisce anche i metodi Math.max(a, b) e Math.abs(x), che restituiscono gli stessi valori.
8.6 Cicli¶
Le istruzioni condizionali scelgono se un blocco di codice viene eseguito; i cicli decidono quante volte viene eseguito.
- I cicli servono a eseguire ripetutamente un blocco di codice finché una condizione è soddisfatta. Come in
C++, i cicli più usati diJavasono il ciclowhilee il ciclofor.
8.6.1 Il ciclo while¶
- Il ciclo
whileesegue un blocco di codice finché la condizione racchiusa fra parentesi()valetrue. Quando la condizione èfalse, il ciclo termina.
Esempio 8.16: ciclo while
int i = 1; // Inizializzazione
while (i <= 5) { // Condizione
System.out.println("Numero: " + i);
i++; // Incremento
}
Output:
8.6.2 Il ciclo for¶
- Il ciclo
forcombina inizializzazione, condizione e incremento (o decremento) in una sola istruzione, esattamente come inC++.
Esempio 8.17: ciclo for
// Inizializzazione; Condizione; Incremento
for (int i = 1; i <= 5; i++) {
System.out.println("Numero: " + i);
}
L'output è lo stesso del ciclo while dell'Esempio 8.16.
8.6.3 Le istruzioni break e continue¶
-
L'istruzione
breaktermina immediatamente il ciclo. -
L'istruzione
continuesalta il resto dell'iterazione corrente e passa alla successiva.
Esempio 8.18: break e continue
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // Salta i numeri pari
}
if (i > 7) {
break; // Interrompe il ciclo al primo dispari maggiore di 7
}
System.out.print(i + " ");
}
System.out.println();
Output:
L'output è lo stesso delle versioni C++ e Python: il valore 9 fa scattare il break.
8.7 Array¶
I cicli diventano davvero utili quando visitano collezioni di valori. Una collezione di variabili dello stesso tipo di dato è memorizzata in un array.
-
Un array in
Javaè una collezione ordinata di elementi dello stesso tipo di dato, con una dimensione fissata al momento della creazione: la dimensione non può cambiare in seguito. -
Il tipo di dato di un array di interi si scrive
int[]. Un array si alloca con l'operatorenew, per esempionew int[n], dove la dimensionenpuò essere decisa durante l'esecuzione, come per gli array di dimensione dinamica diC++. Gli elementi di un nuovo array sono inizializzati a0(afalseperboolean). -
Gli elementi si raggiungono tramite il loro indice fra parentesi quadre
[], a partire da0: gli elementi di un array di dimensione \(n\) sonoa[0],a[1], …,a[n-1]. Ogni array conosce la propria dimensione: la lunghezza di un arrayaèa.length. -
A differenza di
C++, la memoria di un array non deve essere liberata condelete[]: viene rilasciata automaticamente dal garbage collector della JVM quando l'array non è più usato.
Gli array si allocano con new prima dell'uso (oppure si creano con un elenco di valori fra parentesi graffe). Gli indici partono da 0, la dimensione è fissa ed è data da a.length.
Esempio 8.19: creazione e accesso a un array
int n = 5;
int[] a = new int[n]; // Allocazione di un array di n interi
for (int i = 0; i < n; i++) {
a[i] = i * 2; // Esempio di assegnamento di valori
}
System.out.println("Lunghezza: " + a.length);
System.out.println("a[0] = " + a[0]); // Primo elemento
System.out.println("a[4] = " + a[4]); // Ultimo elemento, indice n-1
Output:
Accedere a un indice che non esiste, per esempio a[5], ferma il programma con l'errore ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 (in C++ lo stesso accesso non è controllato).
-
Un array i cui valori sono noti quando si scrive il programma può essere creato elencando i suoi valori fra parentesi graffe, come in
C++. -
Il ciclo
forestesofor (int x : a)visita direttamente gli elementi dell'arraya, senza usare i loro indici, come il cicloforsu una lista diPython. -
Il metodo
Arrays.toString(a), della classeArraysdel pacchettojava.util, restituisce gli elementi di un array come un testo fra parentesi quadre.
Esempio 8.20: array scritto per esteso e iterazione
import java.util.Arrays;
int[] a = {9, 12, 3, 16, 19, 11, 12};
int n = a.length;
for (int i = 0; i < n; i++) { // Ciclo sugli indici
System.out.println("a[" + i + "] = " + a[i]);
}
for (int x : a) { // Ciclo sugli elementi
System.out.print(x + " ");
}
System.out.println();
System.out.println(Arrays.toString(a));
Output:
a[0] = 9
a[1] = 12
a[2] = 3
a[3] = 16
a[4] = 19
a[5] = 11
a[6] = 12
9 12 3 16 19 11 12
[9, 12, 3, 16, 19, 11, 12]
L'istruzione import si scrive all'inizio del file, prima della classe.
8.7.1 Array bidimensionali¶
-
Gli array bidimensionali (matrici) si dichiarano con due coppie di parentesi quadre
[][], e si allocano connew int[m][n](\(m\) righe e \(n\) colonne) in una sola istruzione. L'elemento nella riga \(i\) e colonna \(j\) si raggiunge comematrix[i][j], con entrambi gli indici a partire da \(0\);matrix.lengthè il numero di righe ematrix[i].lengthè il numero di colonne della riga \(i\). -
Un array bidimensionale può anche essere creato elencando i suoi valori riga per riga:
int[][] c = {{3, 1, 4}, {1, 5, 9}};crea un array con \(2\) righe e \(3\) colonne, ec[1][2]è l'elemento nella seconda riga e terza colonna, uguale a \(9\).
Esempio 8.21: array bidimensionale (matrice)
int m = 3; // Numero di righe
int n = 2; // Numero di colonne
int[][] matrix = new int[m][n]; // Allocazione di una matrice m x n
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = i * j; // Esempio di assegnamento di valori
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
System.out.println("matrix[" + i + "][" + j + "] = " + matrix[i][j]);
}
}
Output:
8.7.2 Liste di dimensione variabile: ArrayList¶
-
Quando il numero di elementi non è noto in anticipo, la classe
ArrayListdel pacchettojava.utilfornisce una lista che cresce durante l'esecuzione, come le liste diPython. Il tipo di dato di una lista di interi èArrayList<Integer>: gli elementi di unaArrayListsono oggetti, eIntegerè la classe che corrisponde aint. -
Il metodo
add(x)aggiunge l'elementoxin fondo alla lista,get(i)restituisce l'elemento di indice \(i\),set(i, x)lo sostituisce, esize()restituisce il numero di elementi.
Esempio 8.22: una lista di dimensione variabile
import java.util.ArrayList;
ArrayList<Integer> list = new ArrayList<>(); // Lista vuota
for (int i = 1; i <= 4; i++) {
list.add(i * i); // La lista cresce
}
list.set(0, 100); // Sostituisce l'elemento di indice 0
System.out.println(list + " ha " + list.size() + " elementi");
System.out.println("list.get(2) = " + list.get(2));
Output:
8.8 Metodi (funzioni)¶
Come nello pseudocodice, un algoritmo è scritto come una funzione che riceve l'input e restituisce l'output. In Java le funzioni si chiamano metodi e devono sempre essere definite dentro una classe.
-
Un metodo in
Javaè composto da:-
un modificatore di accesso opzionale (ad esempio
public,private) e la parola chiavestaticquando il metodo appartiene alla classe invece che a un oggetto della classe (tutti i metodi di questo capitolo sonostatic); -
un tipo di ritorno: specifica il tipo del valore restituito (
voidse non viene restituito nulla); -
un nome del metodo: l'identificatore univoco del metodo;
-
un elenco di parametri (opzionale): specifica i valori in ingresso, ciascuno con il suo tipo di dato;
-
un corpo: il blocco di codice eseguito quando il metodo viene chiamato;
-
un'istruzione
return, obbligatoria se il tipo di ritorno non èvoid.
-
-
Il punto di ingresso di ogni programma
Javaè il metodopublic static void main(String[] args), che svolge il ruolo della funzionemaindiC++. Il parametroargscontiene le parole scritte dopo il nome della classe nel comandojava. -
A differenza di
C++, un metodo può essere chiamato prima del punto in cui è definito nella classe: l'ordine dei metodi dentro una classe non conta. -
Il codice sorgente
Javapuò anche essere compilato ed eseguito online, per esempio al seguente indirizzo:
www.mycompiler.io/online-java-compiler
Esempio 8.23: metodi
public class Main {
// Definizione di metodo
static int addition(int a, int b) {
return a + b; // Restituisce la somma
}
// Definizione di metodo
static int multiplication(int a, int b) {
return a * b; // Restituisce il prodotto
}
public static void main(String[] args) {
int sum = addition(5, 10); // Chiamata di metodo
System.out.println("Somma: " + sum);
int prod = multiplication(5, 10); // Chiamata di metodo
System.out.println("Prodotto: " + prod);
}
}
Output:
Esempio 8.24: metodo che opera su un array bidimensionale
public class Main {
static int sumMatrix(int[][] matrix, int rows, int cols) {
int sum = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
sum += matrix[i][j];
}
}
return sum;
}
public static void main(String[] args) {
int m = 3, n = 2;
int[][] matrix = new int[m][n];
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
matrix[i][j] = i * j;
int total = sumMatrix(matrix, m, n);
System.out.println("Somma degli elementi della matrice: " + total);
}
}
Output:
Il corpo di un ciclo for fatto di una sola istruzione può essere scritto senza parentesi graffe, come in C++.
8.8.1 Parametri e valori di ritorno¶
-
In
Javaogni parametro è passato per valore: il metodo riceve una copia dell'argomento. Per i tipi di dato primitivi (int,double,boolean,char) le modifiche fatte alla copia non influiscono sulla variabile di chi chiama; non esiste il passaggio per riferimento con il simbolo&diC++. -
Una variabile di tipo
int[]non contiene gli elementi, ma un riferimento all'array (simile al puntatore diC++). Il metodo riceve una copia del riferimento, che punta allo stesso array: le modifiche fatte agli elementi dell'array sono visibili a chi chiama. -
Un metodo restituisce al più un valore. Quando un metodo deve restituire più risultati, può restituirli in un array, per esempio
new int[]{q, r}.
Esempio 8.25: parametri e risultati multipli
public class Main {
static void increment(int x) {
x = x + 1; // Modifica solo la copia locale
}
static void fillWithZeros(int[] a) {
for (int i = 0; i < a.length; i++) {
a[i] = 0; // Modifica l'array di chi chiama
}
}
static int[] divide(int n, int m) {
return new int[]{n / m, n % m}; // Due risultati in un array
}
public static void main(String[] args) {
int k = 5;
increment(k);
System.out.println("Dopo increment: " + k);
int[] b = {4, 7, 1};
fillWithZeros(b);
System.out.println("Array: " + b[0] + " " + b[1] + " " + b[2]);
int[] qr = divide(17, 5);
System.out.println("Quoziente e resto: " + qr[0] + " " + qr[1]);
}
}
Output:
8.9 Un algoritmo completo: il valore massimo in un array¶
Mettiamo ora insieme tutti i pezzi: un metodo, un ciclo, un'istruzione condizionale e un array.
Consideriamo il problema di trovare il valore massimo in un array \(a\) di \(n \ge 1\) numeri interi.
- Questo è lo pseudocodice dell'algoritmo
FindMaximumdel capitolo «Pseudocodice»: le componenti sono indicizzate da \(1\) a \(n\).
Algoritmo: FindMaximum
-
Questa è l'implementazione in
Java. Gli elementi sono indicizzati da \(0\) a \(n-1\), quindi l'inizializzazione usaa[0]e il ciclo parte dai = 1. La dimensione \(n\) non è passata come parametro, perché il metodo la ottiene cona.length. Il metodo suppone \(n \ge 1\), come lo pseudocodice: per un array vuoto l'accesso ada[0]ferma il programma con unaArrayIndexOutOfBoundsException.public class Main { // Metodo che trova il valore massimo in un array static int arrayMaxValue(int[] a) { int max = a[0]; // Inizializza con il primo elemento for (int i = 1; i < a.length; i++) { if (a[i] > max) { max = a[i]; // Aggiorna il massimo } } return max; } public static void main(String[] args) { int[] a = {9, 12, 3, 16, 19, 11, 12}; int max = arrayMaxValue(a); System.out.println("Valore massimo: " + max); } }Output:
-
L'istanza di prova può anche essere generata a caso con la classe
Randomdel pacchettojava.util: serandè un oggetto di questa classe,rand.nextInt(100)restituisce un intero casuale fra \(0\) e \(99\), quindirand.nextInt(100) + 1è fra \(1\) e \(100\). L'oggetto creato connew Random(2026)parte dal valore fisso \(2026\) (il seme), in modo che ogni esecuzione produca lo stesso array; l'oggetto creato connew Random()produce un array diverso a ogni esecuzione.
Esempio 8.26: un'istanza casuale
import java.util.Arrays;
import java.util.Random;
public class Main {
static int arrayMaxValue(int[] a) {
int max = a[0];
for (int i = 1; i < a.length; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
public static void main(String[] args) {
int n = 10; // Dimensione dell'array
int[] a = new int[n];
Random rand = new Random(2026); // Stesso array casuale a ogni esecuzione
for (int i = 0; i < n; i++) {
a[i] = rand.nextInt(100) + 1; // Intero casuale fra 1 e 100
}
System.out.println(Arrays.toString(a));
System.out.println("Valore massimo: " + arrayMaxValue(a));
}
}
Output:
Esempio 8.27: esecuzione passo per passo
In ogni tabella, la colonna \(i\) è l'indice del ciclo Java e la colonna \(i+1\) è l'indice corrispondente dello pseudocodice. La prima riga è l'inizializzazione max = a[0].
Istanza 1: \(a = [9, 12, 3, 16, 19, 11, 12]\), \(n=7\)
\(i\) (Java) |
\(i+1\) (pseudocodice) | a[i] |
a[i] > max? |
max |
|---|---|---|---|---|
| – | – | – | – | \(9\) |
| \(1\) | \(2\) | \(12\) | Sì | \(12\) |
| \(2\) | \(3\) | \(3\) | No | \(12\) |
| \(3\) | \(4\) | \(16\) | Sì | \(16\) |
| \(4\) | \(5\) | \(19\) | Sì | \(19\) |
| \(5\) | \(6\) | \(11\) | No | \(19\) |
| \(6\) | \(7\) | \(12\) | No | \(19\) |
Restituisce: max \(= 19\)
Istanza 2: \(a = [3, 7, 2, 9, 1]\), \(n=5\)
\(i\) (Java) |
\(i+1\) (pseudocodice) | a[i] |
a[i] > max? |
max |
|---|---|---|---|---|
| – | – | – | – | \(3\) |
| \(1\) | \(2\) | \(7\) | Sì | \(7\) |
| \(2\) | \(3\) | \(2\) | No | \(7\) |
| \(3\) | \(4\) | \(9\) | Sì | \(9\) |
| \(4\) | \(5\) | \(1\) | No | \(9\) |
Restituisce: max \(= 9\)
8.10 Operatori bit a bit¶
Gli operatori bit a bit del capitolo «Introduzione a C++» esistono in Java con gli stessi simboli e lo stesso significato: & (AND), | (OR), (XOR), ~ (NOT), < < (scorrimento a sinistra) e > > (scorrimento a destra). Il metodo Integer.toBinaryString(x) restituisce la rappresentazione binaria di un intero.
-
Un valore di tipo
intha \(32\) bit, quindi~12inverte \(32\) bit e dà-13; il risultato a \(8\) bit diC++si ottiene con la maschera0xFF, come inPython. -
Javaha un terzo operatore di scorrimento,> > >(scorrimento a destra senza segno), che aggiunge sempre zeri a sinistra, anche per i numeri negativi; per i numeri non negativi coincide con> >. -
Poiché una condizione deve essere di tipo
boolean, il test del bit meno significativo si scrive(n & 1) == 1, e non(n & 1)come inC++.
Esempio 8.28: operatori bit a bit
int a = 12; // Binario: 1100
int b = 10; // Binario: 1010
System.out.println((a & b) + " " + (a | b) + " " + (a ^ b)); // AND, OR, XOR
System.out.println(Integer.toBinaryString(a & b)); // 1000
System.out.println((5 << 2) + " " + (20 >> 2) + " " + (20 >> 3)); // Scorrimenti
System.out.println(~a + " " + (~a & 0xFF)); // NOT, e la sua versione a 8 bit
System.out.println((-8 >> 1) + " " + (-8 >>> 28)); // Scorrimento con e senza segno
int n = 15;
System.out.println(((n & 1) == 1) ? "dispari" : "pari"); // Bit meno significativo
Output:
Il numero \(-8\) è memorizzato come \(11\dots11000\) (\(32\) bit): -8 > > 1 mantiene il segno e dà \(-4\), mentre -8 > > > 28 mantiene solo i \(4\) bit più a sinistra \(1111\), cioè \(15\).
8.11 Java, C++ e Python a confronto¶
La tabella seguente riassume le principali differenze fra i tre linguaggi di questa parte del corso.
C++ |
Java |
Python |
|
|---|---|---|---|
| Esecuzione | compilato (g++) |
compilato in bytecode (javac), eseguito dalla JVM (java) |
interpretato (python3) |
| Tipi di dato | dichiarati | dichiarati | non dichiarati |
| Blocchi | {} |
{} |
: e indentazione |
| Fine dell'istruzione | ; |
; |
fine della riga |
| Valori booleani | true, false (stampati 1, 0) |
true, false |
True, False |
10 / 3 |
3 |
3 |
3.3333333333333335 |
-7 / 2 (intero) |
-3 |
-3 |
-7 // 2 vale -4 |
| Intero più grande | \(2^{31}-1\) per int |
\(2^{31}-1\) per int |
nessun limite |
| Operatori logici | &&, ||, ! |
&&, ||, ! |
and, or, not |
| Collezioni | array, liberati con delete[] |
array (a.length) e ArrayList, liberati automaticamente |
liste (len(a)), liberate automaticamente |
| Indice fuori intervallo | non controllato | eccezione | eccezione |
| Più risultati | parametri per riferimento | restituzione di un array | restituzione di una tupla |
| Punto di ingresso | funzione main |
metodo main di una classe |
livello più esterno del file |
Punti chiave del capitolo
-
Un programma
Javaè costituito da classi; è compilato in bytecode conjavaced eseguito dalla JVM conjava, a partire dal metodopublic static void main(String[] args). -
Ogni variabile è dichiarata con il suo tipo di dato, che è controllato dal compilatore; il tipo di dato
intha \(32\) bit, e la divisione fra due interi è la divisione intera. -
Le istruzioni condizionali (
if,else if,else,?:) e i cicli (while,for, e ilforesteso) hanno la stessa sintassi diC++, ma una condizione deve essere di tipoboolean. -
Gli array di
Javasi allocano connew, conoscono la loro dimensionea.length, sono indicizzati da \(0\) a \(n-1\) e sono liberati automaticamente; la classeArrayListfornisce liste di dimensione variabile. -
I parametri sono passati per valore; un metodo che riceve un array può modificarne gli elementi, e più risultati sono restituiti insieme in un array.
Esercizi su questo capitolo: Introduzione a Java