Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
con Java
Lezione 1 - Rudimenti di Programmazione Java
CHE COS’È JAVA?
CARATTERISTICHE FONDAMENTALI
Il progetto Java ha inizio nel 1991 e vede la sua prima implementazione pubblica
nel 1996 con Java 1.0
Quando le classi vengono caricate dalla classe ClassLoader all’interno della JVM
vengono controllate da un Bytecode Verifier che ne verifica la legittimità, ma i
controlli continuano anche runtime.
La JVM
APPLETS
Le applet sono piccoli programmi scritti in Java
Bytecode che si possono trovare sulle pagine
web e che vengono eseguiti dalla JVM.
Le applet:
Java può essere sia compilato che interpretato a seconda dell’ambiente di esecuzione; più
spesso compilato. Rarissime volte può essere anche eseguito dal processore direttamente.
ERRORI (1)
In questo corso useremo sempre il compilatore Java.
Dopo aver scritto codice, potreste incontrare 2 tipi di errori:
● Errori compile-time: Comprendono errori di sintassi e errori logici che il
compilatore rileva mentre traduce. Il compilatore indica a che riga essi
avvengono e spiega cosa non capisce dando alle volte consigli di risoluzione.
Dato che la compilazione effettua vari cicli, alcuni errori potrebbero essere
rilevati solo dopo la correzione di errori precedenti.
ERRORI (2)
● Errori run-time: Errori spesso generati da situazioni non gestite in cui il
programma si trova a lavorare con dati non previsti o risultati che generano
contraddizioni interne al codice.
HELLO WORLD!
STRUTTURA DI UN PROGRAMMA JAVA
Un programma Java è organizzato come un insieme di classi, le quali
corrispondono a una dichiarazione di tipo o a una collezione di funzioni.
Avviate ed eseguite.
Possono rappresentare i campi di una classe o essere usate come variabili locali
dichiarate all’interno di un blocco ({}) di codice.
Possiamo vedere la variabile come una scatola e il suo valore attuale come il
contenuto al suo interno; contenuto che potrà variare più e più volte durante
l’esecuzione.
tipo identificatore;
TIPI PRIMITIVI
Valori che possono essere immagazzinati direttamente nella variabile.
In memoria lo spazio è allocato staticamente così che il processore sa interpretare i dati:
dichiarare una variabile significa infatti allocare spazio in memoria!
Tipi primitivi in Java:
● boolean assume solo valore true o false
● char carattere a 16 bit UNICODE
● byte intero a 8 bit con segno
● short intero a 16 bit con segno
● int intero a 32 bit con segno
● long intero a 64 bit con segno
● float numeri in virgola mobile a 32 bit(IEEE 754)
● double numeri in virgola mobile a 64 bit(IEEE 754)
VARIABILE
Possiamo vederla come una scatola con su apposta un’etichetta.
L’etichetta indica il tipo di dato che può stare all’interno della scatola.
Cessano di esistere quando l’esecuzione raggiunge la fine del blocco dove sono state dichiarare.
IL MODIFICATORE “FINAL”
Un modificatore è una parola che precede il tipo in una dichiarazione (non solo di
variabile) e che ne modifica le proprietà.
Una variabile final, una volta inizializzata (aka, riceve un assegnamento per la
prima volta) non può più essere modificata.
➢ Aritmetici
➢ Incremento/Decremento
➢ Relazionali
➢ Logici
➢ Manipolatori di bit
➢ Assegnamento
➢ Accesso
L’operatore ‘-’ può essere usato singolarmente per invertire il segno: ES: -a
L’operatore può essere prefisso (++i) o postfisso (i++). Nel primo caso viene prima
restituito il valore e poi lo si incrementa. Nel secondo caso prima viene
incrementato e poi restituito.
Restituiscono valori di tipo boolean (true o false). Utilizzabili con i tipi primitivi
numerici come le normali relazioni matematiche.
Non abbiamo visto: ^==xor. Lo xor è un “or esclusivo”: vale true se e solo se un
unico input è true!
SHORT CIRCUIT EVALUATION
★ &&, ||
Importante per impedire operazioni non volute se uno dei due valori confrontati è il
risultato di un metodo!
OPERATORI DI MANIPOLAZIONE DEI BIT
★ &, |, ^, <<, >>, >>>
I primi tre effettuano l’operazione equivalente bit a bit. Es:
0xf00f & 0x0ff0 = 0x000 (0xnnnn è un numero esadecimale)
<<, >>, >>> effettuano operazioni di shift dei bit, ovvero spostano i bit nella
direzione indicata.
● << Sposta i bit a sinistra riempiendo con 0 i bit sulla destra: 001 -> 010
● >> Sposta i bit a destra riempiendo a sinistra con il bit più significativo: 001
-> 000; 101 -> 110
● >>> Sposta i bit a destra riempiendo con 0 i bit sulla sinistra: 101 -> 010
OPERATORI DI ASSEGNAMENTO
Abbiamo già visto come si usa l’operatore ‘=’.
❖ x += 1 equivale a x = x+1
❖ x *= y+1 equivale a x = x * (y+1)
ESERCIZI
● Scrivere un programma che implementi l’espressione ricavata dalla tabella
della verità del primo esercizio di Algebra Booleana della Lezione 0 e che ne
stampi il risultato. Usare la tabella della verità per verificare la correttezza
dell’esecuzione.
OPERATORE DI ACCESSO ‘.’
Serve per accedere a campi e metodi.
● 12 < 12 == 6 > 17
● 0xA2F << 4
● i++ + i++ + --i //i = 3 inizialmente
CONVERSIONI DI TIPO
Java è un linguaggio fortemente tipizzato: vengono effettuati controlli di
compatibilità tra i tipi al momento della compilazione così da evitare che vi siano
assegnamenti incompatibili.
Non sempre sono ammesse (ES: boolean non può essere castato a int). Per effettuare un cast:
In questo modo la perdita di informazione avviene in modo controllato e approssimato il più possibile al
valore originale.
● 289 assegnato a una variabile short (16 bit) in una variabile byte (8 bit).
● 256 assegnato a una variabile short (16 bit) in una variabile byte (8 bit).
● 2559 assegnato a una variabile short (16 bit) in una variabile byte (8 bit).
rif1 == rif2 Se e solo se i due tipi riferimento si riferiscono allo stesso oggetto.
true anche se i due riferimenti sono null (ovvero non esistono, non che puntano a null!)
Gli operatori di uguaglianza controllano l’identità dei riferimenti e non l’equivalenza tra
due oggetti referenziati: due riferimenti sono identici se si riferiscono allo stesso oggetto,
mentre due oggetti sono equivalenti se rappresentano logicamente lo stesso valore.
Molti metodi sono già presenti nelle classi standard di Java… e probabilmente
qualcuno nel mondo ha già creato un metodo che fa ciò di cui avete bisogno.
//corpo metodo
};
return a+b
Un tipo_ritornato speciale è il void, che indica che il metodo non restituisce nulla ma fa solo operazioni interne.
OVERLOADING DEI METODI
Due metodi possono avere lo stesso nome_metodo se hanno segnature diverse,
quindi un numero OR un tipo diverso di parametri.
Si noti che la segnatura non comprende il tipo di valore restituito, quindi non è
possibile sovraccaricare metodi secondo tale concetto.
ES: public static int sum(int i, int y) public double sum(double i, double y) SI!
public static int sum(int i, int y) public double sum(int i, int y) NO!
Spesso è comodo sovraccaricare metodi per accettare più argomenti, mentre farlo
cambiando i tipi può generare confusione. SEMPRE ATTENTI!
RETURN
All’interno del corpo del metodo deve essere presente almeno una volta (e deve
essere raggiungibile) la keyword return.
Essa causa la terminazione del corpo del metodo e restituisce come output ciò
che è indicato alla sua destra.
Il tipo dell’espressione ritornata deve essere lo stesso del tipo_ritornato dichiarato
nell’intestazione del metodo.
Ogni flusso di esecuzione interna deve finire con un return.
Un metodo che ha tipo_ritornato void usa return; per terminare la propria
esecuzione.
INVOCARE UN METODO
I metodi vengono invocati come operazioni sugli oggetti utilizzando i rispettivi
riferimenti e l’operatore ‘.’ visto a slide 44.
riferimento.metodo(argomenti)
Se solo uno dei due operandi è di tipo String, l’altro verrà convertito in modo
implicito in una stringa.
public int charAt(int index) Restituisce il carattere nella posizione index della stringa.
public CharSequence Restituisce una CharSequence (nel nostro caso è una stringa)
subsequence(int start, int end) contenente i valori char dalla posizione start a quella end.
public boolean equalsIgnoreCase(String Come sopra, ma ignora differenze tra minuscole e maiuscole.
s1)
public int compareTo(String s1) Restituisce 0 se le stringhe sono uguali, un numero positivo se
la stringa su cui è invocato il metodo segue in ordine
lessicografico quella su cui è invocato e negativo se la precede
public boolean startsWith(String prefix, true se la stringa su cui è invocato il metodo comincia con
int start) prefix (a partire da start, che è omissibile)
public boolean endsWith(String suffix) true se la stringa su cui è invocato il metodo termina con suffix.
COSTRUZIONE DI STRINGHE CORRELATE
public String replace(char oldChar, char Restituisce una nuova String in cui tutti i caratteri oldChar sono
newChar) sostituiti con newChar.
public String trim() Restituisce una nuova String in cui sono rimossi tutti i caratteri
di spaziatura all’inizio e alla fine ( “ ”, “\t”, “\n”)
public String replaceFirst(String regex, Restituisce una nuova String in cui la prima sottostringa che
String repStr) corrisponde a regex viene sostituita da repStr.
public String replaceAll(String regex, Come sopra, ma sono sostituite tutte le sottostringhe.
String repStr)
public String toLowerCase() Restituisce una nuova String equivalente a quella su cui viene
invocato il metodo, ma tutta minuscola.
public StringBuilder insert(int offset, String Inserisce s nella stringa dopo offset caratteri.
s)
public StringBuilder replace(int start, int Rimpiazza i caratteri da start a end (escluso) con s. Se s è
end, String s) più o meno lunga di end-start, la stringa viene adattata.
public String toString(StringBuilder sb) Crea una String dal valore attuale di sb
public void ensureCapacity(int min) Assicura che la capacità del riferimento sia almeno min.
public boolean equals(Object ob) Verifica se l’oggetto riferito è uguale a quello riferito da ob.
public String toString() Restituisce uno String che rappresenta l’oggetto. Di base la stringa è
composta da nome della classe, @ e il codice hash in Hex.
CLASSE INVOLUCRO (WRAPPER CLASS)
Una per tipo base, sono classi che contengono un valore del tipo primitivo
corrispondente. Conversioni tra tipi primitivi e loro wrapper class sono impliciti,
ad ES: Integer val = 3;
Qui di seguito, Type si riferirà alla classe involucro del tipo associato, type al
corrispondente tipo primitivo.
COSTRUZIONE
Tutte i Type hanno il seguente costruttore:
public Type(type tipo) Costruisce un Type a partire dal valore “tipo” di tipo type.
public static Type valueOf(type tipo) Restituisce un oggetto di tipo Type di valore tipo.
COSTANTI INVOLUCRO
Ogni classe involucro definisce i tre seguenti campi:
public static final type MIN_VALUE Minimo valore rappresentabile per type. Per int è -231
public static final type MAX_VALUE Massimo valore rappresentabile per type. Per int è 231-1
public static final int SIZE Numero di bit utilizzati per rappresentare un valore di questo
tipo. Per int è 32 (4 byte)
METODI INVOLUCRO
public int compareTo(Type other) Restituisce 0 se l’oggetto riferito ha valore uguale a quello
puntato da other, <0 se ha valore minore e >0 se ha valore
maggiore.
public static parseType(String s) Converte s al valore del tipo primitivo type considerato.
Character non ha questo metodo.
public String toString() Restituisce rappresentazione sotto forma di stringa del valore
dell’oggetto involucro.
public static String toString(type val) Restituisce rappresentazione sotto forma di stringa di val.
CONVERSIONI
TIPO DA STRING A TIPO DA TIPO A STRING
Character // toString(char c)
Vedremo più avanti un tipo di Input più comodo, ma per ora vediamo Scanner in
modo ridotto ma funzionale.
L’istruzione standard per importare una libreria in Java è la keyword import e deve essere dichiarata prima della keyword class.
import package/Classe;
import è ignorabile se la Classe si trova nella stessa directory della classe chiamante.
La keyword * sostituisce una qualsiasi occorrenza dell’import con tutto ciò che è contenuto a quel livello del package. ES:
import java.util.* Importa tutto il contenuto del pacchetto util del pacchetto java.
import java.util.Scanner;
L’argomento di Scanner è la sorgente dei dati. Per ora ci limiteremo a System.in: la linea di comando
dove System.out scrive.
LEGGERE DA INPUT
Una volta che lo Scanner è pronto, non ci resta che chiedere gli input all’utente. Lo
Scanner leggerà ciò che viene scritto da tastiera, ma deve sapere come
interpretarlo. I metodi che effettuano la lettura hanno forma:
public Type nextType()
Per le stringhe, il metodo sarà nextLine().
La classe “Echo”
CHIUDERE LO SCANNER
Ricordate sempre di chiudere lo scanner dopo averlo aperto!
nomeScanner.close()
ESERCIZI
● Scrivere e provare la classe Echo.
● Usando come base gli esercizi precedenti, modificarli in modo tale che tutti i
valori inizializzati dal codice vengano invece inizializzati dall’utente.
● Scrivere un programma che legga un valore numerico da standard input ma
che lo consideri come una stringa e che quindi ne faccia il quadrato.
Stampare il risultato a schermo.
ARRAY
ARRAY
Un array è una collezione di variabili tutte dello stesso tipo a cui è possibile
accedere mediante semplici indici interi. In Java, gli array sono oggetti.
DICHIARAZIONE: type[] nomeArr = new type[dim] (type nomeArr[])
La dichiarazione vista crea un oggetto di tipo type[] (ovvero, array di tipo type) di
dimensione dim e ne restituisce il riferimento a nomeArr.
Il numero di componenti appartenenti all’array viene determinato al momento della
sua creazione e non della dichiarazione della variabile: nomeArr può cambiare per
riferire un altro array di tipo type di qualsiasi altra lunghezza.
Gli array hanno lunghezza fissa.
ACCESSO
L’accesso ad un array avviene in base alla posizione che i suoi elementi
occupano al suo interno.
…
INIZIALIZZAZIONE
Quando si crea un array, ogni suo elemento viene inizializzato al valore di default
per il suo tipo. I numeri in genere sono 0, mentre gli oggetti sono null!
L’inizializzazione può essere effettuata mediante le {}:
type[] nomeArray = {val1, val2, val3,...}
ES: String[] animali = {“cane”, “gatto”} Crea array di lunghezza 2.
animali[0] valore: “cane”
animali[1] valore: “gatto”
Simile discorso vale per gli array multidimensionali.
OPERATORI DI INC/DEC E ARRAY
Attenzione a quando si usano gli operatori ++ e -- con gli array!
Come si passano tipi base? NON SI PASSANO. Gli args sono solo stringhe!
I blocchi sono insiemi di 0+ più istruzioni chiuse tra { e }. Un blocco può essere usato laddove si
può utilizzare un’istruzione singola poiché è considerato come un’istruzione composta.
IF-ELSE
if (espressione) istruzione1
else istruzione2
espressione viene valutata come boolean. Se è:
➢ true: esegue istruzione1
➢ false: esegue istruzione2
else è opzionale: se manca e espressione è false, il blocco if non viene eseguito.
istruzione1 e istruzione2 possono essere blocchi.
Possibile aggiungere un altro if all’else, creando l’if-else if-else. Non c’è limite al numero
di if concatenabili.
IF-ELSE IF-ELSE IF-ELSE...
ES:
if (espressione1) istruzione1;
Notare che l’ultimo if (espressione) poteva essere omesso a favore del solo else.
ESERCIZI
● Scrivere una classe che legge un intero e che stampa “Voto non valido” se
esso non è compreso tra 0 e 30 (compresi). Altrimenti non fa nulla.
● Scrivere un programma che legge un double e che determini se si tratta di un
numero pari o dispari. Stampare a schermo l’esito.
● Implementare l’algoritmo 2 della Lezione 0 (quale dei 3 numeri è maggiore?)
● Implementare l’algoritmo 4 della Lezione 0 (aree di rettangoli)
ESERCIZIO
● Scrivere una classe FrazioniEquivalenti.java che verifica se due frazioni
num1/den1 e num2/den2 sono equivalenti e stampa "equivalenti" o "non
equivalenti", a seconda del caso. Usare il tipo int per i dati in input. Pensate a
una soluzione che non faccia uso di float/double.
OPERATORE ?
Detto operatore condizionale, fornisce un’espressione singola in grado di produrre uno
tra due valori in base alla valutazione di un’espressione di tipo boolean.
corrisponde a:
if (userSetIt)
value = usersValue;
else
value = defaultValue;
SWITCH
switch (espressione) {
case n: istruzioni
case m: istruzioni
…
default: istruzioni
}
Si trasferisce il controllo al punto identificato dall’etichetta che corrisponde al risultato
dell’espressione: essa deve essere di tipo intero(byte, short, int o char o una loro classe
involucro) o di tipo enumerativo.
“BLOCCO SWITCH”
Istruzioni del costrutto switch che sono precedute da un’etichetta case associata alla
quale vi è una costante intera o enumerativa. Se il valore di espressione è uguale a una
di queste costanti, il controllo viene passato alla prima istruzione dopo quell’etichetta.
Un’istruzione singola può avere associate più etichette case nel caso debba essere
eseguita in più situazioni:
case n: case m: case i:
istruzione
Se nessuna etichetta corrisponde al valore di espressione, il controllo è passato a alla
prima istruzione che segue l’etichetta default.
Se non vi è un’etichetta default, il blocco switch viene saltato.
INTERROMPERE IL BLOCCO SWITCH
ATTENZIONE! Quando il controllo è passato ad un’etichetta, le istruzioni vengono
eseguite una alla volta dopo quell’etichetta e proseguiranno fino alla fine del
blocco switch!
Se si vuole evitare ciò, al termine delle istruzioni seguenti l’etichetta bisogna
aggiungere la keyword break (le cui applicazioni vedremo più dettagliatamente più
avanti). Incontrare tale parola passa il controllo alla prima istruzione dopo lo
switch.
Se lo switch è definito all’interno di un metodo e le istruzioni di un case
determinano il valore di ritorno del metodo, return può sostituire funzionalmente
break,
ESEMPIO SWITCH
ESERCIZI
● Modificare la classe VocaleConsonante così che gestisca altri casi, quali:
○ ‘c’: ogni lettera ‘c’ di s viene modificata con ‘k’ e a schermo si stampi il valore della nuova
stringa,.
○ ‘y’: si verifica la seconda lettera di s e se essa è una vocale si la si sostituisce con 5, altrimenti
la si cambia con ‘9’.
○ ‘l’: si legge a schermo una stringa e la si confronta con quella originale verificando che siano la
stessa stringa. Stampare a schermo “ok” se sono uguali e “nope” se non lo sono.
● Scrivere una classe NumGiorni che, dato un intero da tastiera, lo interpreti
come un mese dell’anno e stampi a schermo il numero di giorni
corrispondenti. Ad esempio, 11 = Novembre, quindi stampo “30”. Se il valore
dato è fuori dal range [0,12], stampare un messaggio d’errore. In caso il mese
sia Febbraio, chiedere all’utente se l’anno è bisestile o meno e a seconda
della risposta restituire il valore corretto!
WHILE
while (espressione)
istruzione
Il while è un ciclo che ripete l’istruzione finché il valore di espressione (di tipo
boolean o Boolean) risulta true. Il valore è controllato ogni volta che l’ultima
istruzione del blocco viene eseguita.
Quando espressione vale false, il controllo viene dato alla prima istruzione dopo il
while.
istruzione
while(espressione);
La base è la più potente e generale delle due e effettua un ciclo dal principio sino alla fine di un intervallo di valori.
istruzione
Inizializzazione dichiara e/o le variabili di ciclo ed è un’espressione eseguita una sola volta. Possono essere
dichiarate più variabili separate da virgola. Possono essere dichiarate nuove variabili che valgono sono per il ciclo.
Ciclo è un’istruzione booleana valutata prima di eseguire una iterazione: se è true essa viene eseguita, altrimenti no.
Aggiornamento è eseguita al termine di ogni iterazione ed è un’espressione che ha l’obiettivo di aggiornare le variabili
di ciclo.
FOR (2)
Ciclo spesso utilizzato per poter scorrere all’interno di collezioni come gli array.
etichetta: istruzione
● break;
● continue;
BREAK
break permette di uscire da un qualsiasi blocco di codice. Esiste in due forme:
return val;
Se il metodo non restituisce nulla (aka void) allora si può scrivere return;
JAVA API
API
Le Application Programming Interfaces sono interfacce computazionali (punti di
contatto “logici”) che permettono di far comunicare il vostro codice a specifici SW.
Esse definiscono tutte le chiamate che possono essere fatte ai servizi proposti da tali
SW, come devono essere fatte, quali dati devono essere passati e come.
Java mette a disposizione diversi pacchetti standard per permettere tali interazioni, tutti
quanti racchiusi sotto il package java. Essi contengono Classi, eccezioni e altro.
È possibile trovare la lista completa nella documentazione al seguente link:
https://docs.oracle.com/en/java/javase/15/docs/api/index.html
In questo corso vedremo ne vedremo solo alcuni.
java.io e java.nio
java.io contiene le classi per la gestione dell’I/O, oltre che la manipolazione del
file system. ✓
Esse hanno vengono caricate nel browser da un metodo init() che viene invocato
quando la pagina web viene aperta, seguita da un metodo start() che ne fa
effettivamente eseguire le istruzioni.
Se il browser identifica che l’applet non è più utile all’utente (magari perché ha
chiuso la pagina) esso invoca stop() che ne arresta l’esecuzione. Se è deciso che
l’applet non verrà più utilizzata, essa verrà chiusa dall’operazione destroy(),
altrimenti verrà fatta ripartire con start().
java.net e java.sql
java.net fornisce classi che permettono di gestire le infrastrutture necessarie per
gestire il networking come le socket, gli indirizzi di rete, gli URI e gli URL. χ
METODI STATICI:
PRECAUZIONI D’USO: