Codice: LMP · CFU: 12 · Semestre: 1-2 · Anno: 2° SSD: ING-INF/05 Docenti: Zanzotto, Stellato Propedeuticità: Programmazione dei Calcolatori con Laboratorio
L'esame si svolge direttamente a giugno/luglio e non ha esoneri. È diviso in due moduli:
- Modulo I — Metodologie di Programmazione e Java (Zanzotto)
- Modulo II — Programmazione Dichiarativa e Prolog (Stellato)
Riferimento tassonomico degli argomenti da conoscere per padroneggiare Java. Ogni voce è un argomento autonomo, non un'attività da spuntare. L'ordine è didatticamente coerente ma pensato per consultazione non lineare.
- Java Virtual Machine (JVM)
- Java Runtime Environment (JRE)
- Java Development Kit (JDK)
- Bytecode e modello di esecuzione
- Ciclo di rilascio e versioni LTS (8, 11, 17, 21)
- Distribuzioni (OpenJDK, Temurin, Oracle JDK, GraalVM)
- Compilatore
javac - Launcher
java - Jar e Jar eseguibili (
jar) - Classpath e module path
javadocper la documentazione generata
- IntelliJ IDEA
- Eclipse
- Visual Studio Code con estensione Java
- Editor minimali (Vim, Emacs) con LSP
- Convenzione
src/eout/(obin/) - Classpath e organizzazione in package
- Module system (
module-info.java) - File
.iml(IntelliJ) e.classpath/.project(Eclipse)
- Interi:
byte,short,int,long - Virgola mobile:
float,double charboolean- Valori di default e intervalli numerici
- Letterali numerici (decimali, esadecimali, binari, con separatore
_) - Caratteri di escape
- Concetto di reference vs valore
nulle suo ruolo nel sistema di tipiStringcome tipo speciale
- Dichiarazione e inizializzazione
- Scope (blocco, metodo, classe)
finalper l'immutabilità del riferimentovarper l'inferenza di tipo locale (Java 10+)
- Aritmetici e di assegnamento composto (
+=,-=, ...) - Confronto e uguaglianza
- Logici booleani (
&&,||,!) - Bitwise e di shift
- Pre e post incremento/decremento
- Operatore ternario (
? :) - Precedenza e associatività
if/else if/elseswitchstatement (classico)switchexpression (Java 14+)- Pattern matching in
switch(Java 21+) while,do-whileforclassicofor-each(enhanced for)break,continue, labelreturn
- Dichiarazione e inizializzazione
- Accesso per indice
- Proprietà
length - Array multidimensionali
- Array literal
- Firma (nome, parametri, tipo di ritorno),
voidvs tipi di ritorno, parametri formali/attuali, passaggio per valore anche per i reference - Overloading: regole di selezione della firma, ambiguità da evitare
- Varargs (
Type... args) - Ricorsione: casi base e ricorsivi, limiti dello stack
- Definizione: keyword
class, modificatori top-level, relazione tra nome del file e classepublic - Campi di istanza; inizializzazione inline, nel costruttore, in blocchi; modificatore
final - Costruttori: default, sovraccaricati,
this(...),super(...), privati (singleton/utility) - Metodi di istanza,
this, getter/setter, metodi privati di supporto - Ciclo di vita:
new, riferimenti multipli, garbage collector (concetto),finalize(cenno storico) - Metodi ereditati da
Object:toString,equals,hashCode(contratto),getClass,clone
- Modificatori:
public,private,protected, package-private - Campi privati con accessor pubblici, invarianti di classe, information hiding
- Campi statici, inizializzazione statica, blocchi
static { ... } - Metodi statici, utility class,
main, limitazioni (nientethis) - Costanti: idioma
public static final, namingSCREAMING_SNAKE_CASE
extends, single inheritance tra classi,Objectcome radice- Override: regole,
@Override, covarianza del tipo di ritorno, visibilità super.metodo(...),super(...)- Polimorfismo: upcast implicito, dispatch dinamico, downcast esplicito,
instanceofcon pattern matching (Java 16+) - Limiti:
final, composition over inheritance, Liskov
abstract class, metodi astratti e concreti insieme- Impossibilità di istanziare, obbligo delle sottoclassi, template method
interface, metodi astratti impliciti, costantipublic static final, ereditarietà multipla tra interfacceimplements, implementazione multipla, combinazione conextends- Evoluzione:
default,static,private(Java 9+) - Funzionali:
@FunctionalInterface, SAM, lambda
- Semplici: sintassi,
values(),valueOf(String),ordinal(),name(), uso inswitch - Avanzati: campi, costruttori, metodi di istanza, corpo per ogni costante, interfacce
- Sintassi
record, componenti e accessor generati, costruttori compatti/canonici, immutabilità strutturale, quando usarli
sealed,non-sealed,permits, restrizione delle gerarchie, pattern matching, confronto con enum/interfacce
- Gerarchia:
Throwable→Error/Exception→RuntimeException; checked vs unchecked - Sintassi:
try/catch/finally,throw,throws, multi-catch, try-with-resources,AutoCloseable - Custom: estendere
Exception/RuntimeException, costruttori standard, chaining,serialVersionUID - Pratiche: quando checked vs unchecked,
getMessage/getCause/printStackTrace, anti-pattern catch silenzioso
- Tipi parametrici: classi/interfacce/metodi generici, diamond operator
- Bounds: upper bound, wildcard
?,? extends T,? super T, PECS - Limitazioni: type erasure, array di tipi generici impossibili,
Class<?>vsClass<T>, tipi reificabili
- Interfacce:
Iterable,Collection,List,Set,SortedSet,NavigableSet,Queue,Deque,Map,SortedMap,NavigableMap - Implementazioni:
ArrayList,LinkedList,HashSet,LinkedHashSet,TreeSet,HashMap,LinkedHashMap,TreeMap,ArrayDeque,PriorityQueue,Collections.unmodifiable* - Iterazione:
Iterator/Iterable, for-each,forEachcon lambda,ConcurrentModificationException - Operazioni: inserimento/rimozione/ricerca,
Map.Entry/entrySet/keySet/values,Collections.sort, factory immutabili (List.of,Map.of)
- Lambda: sintassi
(params) -> expre{ body }, inferenza, variabili catturate (effectively final) - Method reference:
Class::staticMethod,instance::method,Class::instanceMethod,Class::new - Interfacce funzionali standard:
Function,Predicate,Consumer,Supplier, variantiBiX,UnaryOperator,BinaryOperator, varianti primitive
- Creazione: da collezioni,
Stream.of,generate,iterate,Arrays.stream,Files.lines - Intermedie:
filter,map,flatMap,sorted,distinct,limit,skip,peek,mapToInt,boxed - Terminali:
forEach/forEachOrdered,collect(toList,toSet,toMap,groupingBy,partitioningBy,joining,counting),reduce,count/min/max,findFirst/findAny,anyMatch/allMatch/noneMatch - Primitivi:
IntStream/LongStream/DoubleStream, operazioni statistiche - Parallel:
parallelStream(), quando ha senso
- Creazione:
of,ofNullable,empty - Ispezione:
isPresent,isEmpty,ifPresent,ifPresentOrElse - Trasformazione:
map,flatMap,filter - Estrazione:
orElse,orElseGet,orElseThrow - Quando non usarlo (campi, parametri di metodo)
Comparable<T>ecompareTo,Comparator<T>ecompareComparator.comparing,thenComparing,reversed- Ordinamento naturale vs esplicito, relazione con
equals/hashCode
- Stream byte/caratteri:
InputStream/OutputStream,Reader/Writer, buffered,FileReader/FileWriter,InputStreamReader/OutputStreamWriterper encoding - File e path:
java.io.File(legacy),java.nio.file.Path/Paths/Files,Files.readAllLines/lines/writeString, attributi/permessi - Try-with-resources per I/O, gestione eccezioni checked
- Serializzazione:
Serializable,serialVersionUID, alternative moderne (JSON, Protobuf, custom) - Console:
Scanner,System.console(),PrintWriter,printf
Class<T>:forName,.class,getClass- Introspezione:
getFields/getDeclaredFields,getMethods/getDeclaredMethods,getConstructors/getDeclaredConstructors, annotazioni runtime,setAccessible(true) - Istanziazione:
Constructor.newInstance,Method.invoke - Eccezioni:
ClassNotFoundException,NoSuchMethodException,NoSuchFieldException,InvocationTargetException,InstantiationException,IllegalAccessException - Casi d'uso: DI, ORM, serializzatori, plugin loading
- API legacy
java.util.Date/Calendar(panoramica storica) - Moderna
java.time:LocalDate/LocalTime/LocalDateTime,ZonedDateTime/OffsetDateTime,Instant/Duration/Period,DateTimeFormatter,ChronoUnit
String: immutabilità, metodi principali (length,charAt,substring,split,trim/strip,replace,indexOf,contains,equals,equalsIgnoreCase,startsWith,endsWith),compareTo, pool eintern()- Costruzione:
StringBuilder,StringBuffer,String.format/printf, text block (Java 15+) - Conversioni:
parseInt/parseDouble,String.valueOf, autoboxing
- Thread:
Thread/Runnable, ciclo di vita,startvsrun,join/sleep/interrupt - Sincronizzazione:
synchronized,volatile, atomic (AtomicInteger,AtomicReference), deadlock/livelock/starvation - Executor:
Executor/ExecutorService,Future/Callable,CompletableFuture, thread pool - Virtual threads (Java 21+)
module-info.java, direttiveexports/requires/opens/uses/provides- Moduli nominati, automatici, unnamed
- Encapsulamento forte tra moduli
- Quando adottarlo e quando no
- Principi: SOLID, DRY, KISS, YAGNI, composition over inheritance, immutabilità come default
- Pattern ricorrenti: Factory method, Abstract factory, Builder, Singleton, Strategy, Observer, Template method, Decorator, Adapter
- Convenzioni: naming (PascalCase/camelCase/SCREAMING_SNAKE_CASE/package minuscoli con dominio invertito), package per feature o layer, convenzione
Interface/Impl, Javadoc
- JUnit 5:
@Test,@BeforeEach/@AfterEach/@BeforeAll/@AfterAll, assertions (assertEquals,assertThrows,assertAll),@ParameterizedTest, lifecycle - Pratiche: Arrange-Act-Assert, unitari vs integrazione vs end-to-end, Mockito (cenno), coverage e sue insidie
- Maven: ciclo di vita,
pom.xml, dipendenze/scope/versioning, repository centrale - Gradle:
build.gradle/.kts, task e plugin, wrapper (gradlew) - Librerie comuni: SLF4J, Guava, Apache Commons, Jackson; gestione versioni
java.util.logging(cenno), SLF4J come API di facciata, Logback e Log4j2 come implementazioni- Livelli: TRACE, DEBUG, INFO, WARN, ERROR
- Buone pratiche: niente
System.outin produzione
- Annotazioni custom e processing
- Proxy dinamici (
java.lang.reflect.Proxy) - Classloader
- JNI (cenno)
- Foreign Function & Memory API (Java 21+, cenno)
- Jshell (REPL ufficiale)
- JPMS e applicazioni modulari reali
- Introduzione ai framework (Spring, Jakarta EE, Quarkus) come destinazione naturale dopo Java core
Guida pensata per chi parte da zero e vuole arrivare al livello degli esercizi del corso senza salti logici.
La guida è divisa in dodici fasi progressive. Ogni fase ha:
- Obiettivo — cosa saprai fare alla fine
- Teoria — concetti da comprendere prima del codice
- Tracce di esercizio — problemi concreti da risolvere (fanne almeno due per fase)
- Auto-verifica — domande cui rispondere senza IDE
- Errori comuni — trappole
- Consiglio — suggerimento di metodo
Regola d'oro: non passare alla fase successiva finché non hai scritto tu, da zero, almeno un programma che compila ed esegue per quella fase. Leggere non è studiare.
Prerequisiti: computer con Linux/macOS/Windows, terminale, zero Java richiesto.
Ambiente:
- Installa JDK 21 (Temurin o OpenJDK). Verifica con
java -versionejavac -version. - Installa IntelliJ IDEA Community Edition.
- In IntelliJ:
File → New Project → Java → Project SDK = 21, nessun build tool. - Verifica che un
Hello Worldcompili ed esegua.
Mentalità: Java è verboso. All'inizio ti sembrerà di scrivere molto codice per fare poco: è normale. La verbosità porta il type system, che ti protegge da un'intera classe di errori. Impara a leggere i messaggi del compilatore: sono il tuo tutor personale.
Obiettivo: scrivere programmi procedurali semplici con variabili, condizioni, cicli e metodi.
Teoria: struttura di un file .java e main, tipi primitivi e String, variabili e final, operatori, if/else/switch, while/for, array monodimensionali, metodi static.
Tracce:
- Calcolatrice da riga di comando. Due numeri + operatore da
args[], gestione divisione per zero. - FizzBuzz. 1-100, "Fizz"/"Buzz"/"FizzBuzz".
- Statistiche array. Min, max, media, pari.
- Indovina il numero. Casuale 1-100, 7 tentativi,
Scanner.
Auto-verifica: differenza int/Integer? Perché "abc" == "abc" può funzionare ma new String("abc") == new String("abc") no? Cosa stampa System.out.println(3 / 2)? A cosa serve static in main?
Errori comuni: confronto stringhe con ==, divisione intera tronca, off-by-one.
Consiglio: quando il compilatore ti urla, leggi il messaggio per intero prima di cercare su Google.
Obiettivo: capire classe/oggetto, modellare un concetto reale come classe.
Teoria: classe vs istanza vs oggetto, campi/metodi, costruttori, this, public/private, incapsulamento, getter/setter, override di toString(), package.
Tracce: Libro (titolo/autore/anno/pagine, toString), Rettangolo (area/perimetro/isQuadrato), ContoCorrente (deposita/preleva con controllo saldo).
Auto-verifica: perché campi private? Cosa succede senza costruttore? Differenza classe/oggetto? Cosa fa this.nome = nome?
Errori comuni: campi public, dimenticare inizializzazione, non fare new.
Consiglio: prima di scrivere codice, disegna la classe su carta.
Obiettivo: separare cosa un oggetto sa fare (interfaccia) da come lo fa (implementazione).
Teoria: interface, implements, convenzione Foo/FooImpl, dichiarare per interfaccia (Forma f = new Cerchio(...)).
Tracce: Forma con area/perimetro implementata da Cerchio/Rettangolo/TriangoloRettangolo; Animale con nome/verso; Descrivibile implementata da classi diverse (Libro, Automobile).
Auto-verifica: puoi istanziare un'interfaccia? Vantaggio di dichiarare per interfaccia? Più interfacce? Più classi estese?
Errori comuni: public ridondante, dimenticare implements, logica nei metodi dell'interfaccia.
Consiglio: se una classe potrebbe avere varianti, estrai un'interfaccia prima di continuare.
Obiettivo: costruire gerarchie dove le sottoclassi riutilizzano ed estendono.
Teoria: extends, super(...)/super.metodo(), override e @Override, polimorfismo e dispatch dinamico, overloading vs overriding.
Tracce: Dipendente → Manager/Operaio con stipendioMensile() ridefinito; Veicolo → Auto/Moto/Camion; Studente → StudenteArrabbiato.
Auto-verifica: overloading vs overriding? Cosa fa super(nome)? Con Dipendente d = new Manager(...) quale metodo si esegue? Perché @Override è utile?
Errori comuni: dimenticare super(...), credere che si esegua il metodo del tipo dichiarato, rompere il principio di sostituzione.
Consiglio: usa sempre @Override. Se il compilatore si lamenta, hai sbagliato la firma.
Obiettivo: gerarchia dove la base impone struttura ma lascia alle sottoclassi i dettagli.
Teoria: abstract class, metodi abstract, protected, campi/metodi static, contatori statici, differenza abstract class vs interface.
Tracce: Account astratto con calcolaInteresse() implementato da ContoRisparmio/ContoCorrente; Utente con contatore statico; figure geometriche v2 con classe astratta.
Auto-verifica: quando classe astratta vs interfaccia? Puoi fare new di una astratta? A chi appartiene un static? Un metodo static può usare this?
Errori comuni: static dove andrebbe istanza (e viceversa), classe astratta usata solo per bloccare l'istanziazione (anti-pattern), mischiare static ed ereditarietà.
Consiglio: se una classe astratta non ha campi né metodi concreti, probabilmente volevi un'interfaccia.
Obiettivo: modellare insiemi chiusi di valori in modo type-safe.
Teoria: enum semplici, enum con campi/costruttore/metodi, values()/valueOf/ordinal/name, enum che implementano interfacce.
Tracce: Giorno con isWeekend(); StatoOrdine con puoEssereAnnullato(); Pianeta con gravitaSuperficie().
Auto-verifica: == o .equals() per enum? MyEnum.valueOf("NON_ESISTE")? Metodi su enum?
Errori comuni: costanti int al posto di enum, dimenticare ; dopo l'ultimo valore con campi/metodi, modificare stato di un enum.
Consiglio: ogni volta che scrivi if (s.equals("APERTO") || s.equals("CHIUSO")), fermati: quello è un enum.
Obiettivo: gestire errori in modo pulito con tipi dedicati.
Teoria: gerarchia Throwable → Error/Exception → RuntimeException, checked vs unchecked, try/catch/finally, throw vs throws, multi-catch, eccezioni custom, exception chaining, try-with-resources (cenno).
Tracce: ContoCorrente v2 con SaldoInsufficienteException (checked) e ImportoNegativoException (unchecked); registraUtente con EtaNonValidaException; parser di numeri con ParseNumeriException che incapsula NumberFormatException.
Auto-verifica: checked vs unchecked? Se non catturi una checked? Anti-pattern del catch (Exception e) silenzioso? Causa nel costruttore dell'eccezione?
Errori comuni: catturare Exception e non fare nulla, eccezioni per controllo di flusso, gerarchie troppo profonde.
Consiglio: un'eccezione lanciata va sempre gestita o propagata. Mai nascosta.
Obiettivo: usare le strutture dati standard di Java in modo type-safe.
Teoria: Collection/List/Set/Map, ArrayList/HashMap/HashSet, generics <T> e diamond operator, for-each, add/get/remove/contains/size, Map.Entry/entrySet/keySet/values.
Tracce: Rubrica telefonica; Conteggio parole; Magazzino con scarico e prodotti esauriti; Università v2 con Map<String, Studente> per matricola.
Auto-verifica: List vs Set? Perché HashMap richiede hashCode buono? Type erasure a runtime? Iterare le entry di una Map?
Errori comuni: List<Object> invece di List<Tipo>, modificare collection mentre la iteri (ConcurrentModificationException), chiavi con ==.
Consiglio: dichiara per interfaccia, istanzia per implementazione. Cioè List<String> l = new ArrayList<>();.
Obiettivo: leggere/scrivere file di testo, parsarli in strutture dati.
Teoria: File/FileReader/BufferedReader, lettura riga per riga, try-with-resources, manipolazione stringhe (split/trim/toUpperCase), Integer.parseInt/NumberFormatException, alternativa java.nio.file.Files.
Tracce: Config reader (chiave=valore, ignora vuoti e commenti #); CSV semplice con CsvParseException; Log filter con conteggi INFO/WARN/ERROR.
Auto-verifica: BufferedReader vs FileReader? Try-with-resources? Perché readLine() ritorna null? java.io vs java.nio.file?
Errori comuni: non chiudere il file, ignorare FileNotFoundException, split con separatore nel valore.
Consiglio: il bug più comune dei parser è non considerare input rotto. Chiediti cosa succede se manca una riga, c'è un carattere strano, il file è vuoto.
Obiettivo: capire l'introspezione a runtime e quando è utile.
Teoria: Class<?> e Class.forName, .class, getDeclaredConstructors/Fields/Methods, Constructor.newInstance(...), eccezioni (ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException).
Tracce: Plugin factory (istanziazione da nome classe); Stampatore generico che elenca i campi di un oggetto via reflection.
Auto-verifica: perché è lenta? Cosa rompe della type safety? Casi d'uso reali? getDeclaredConstructors vs getConstructors?
Errori comuni: reflection quando basta il polimorfismo, ignorare la cascata di eccezioni, assumere un ordine specifico degli array restituiti.
Consiglio: reflection è l'ultima spiaggia, non la prima scelta.
Obiettivo: mettere insieme tutto. Nessuna teoria nuova, solo integrazione.
Traccia: gestionale biblioteca da riga di comando
Input libri.txt con blocchi separati da ---:
tipo:cartaceo
titolo:Il nome della rosa
autore:Umberto Eco
anno:1980
copie:3
---
tipo:ebook
titolo:1984
autore:George Orwell
anno:1949
formato:epub
---
Classe astratta Libro, sottoclassi LibroCartaceo e LibroDigitale; enum Genere (opzionale); Biblioteca con Map<String, Libro> e metodi presta/restituisci/cercaPerAutore/libriDisponibili; eccezioni custom LibroNonTrovatoException/LibroNonDisponibileException/FormatoFileException; factory basata su tipo:; Main con loop interattivo (presta/restituisci/cerca/lista/esci).
Obiettivo pedagogico: nel codice finale deve esserci evidenza di ereditarietà e classe astratta, enum, collections + generics, eccezioni custom, I/O da file, incapsulamento corretto, separazione in package (model, io, app).
Consiglio: non scrivere tutto di filato. Parti dalla classe Libro, poi Biblioteca con due libri hardcoded, poi file reader, poi loop interattivo, poi eccezioni, poi raffina. Una feature alla volta, compilando e testando ad ogni passo.
Obiettivo: conoscere le feature post-Java-8 che nel corso non sono usate ma che trovi ovunque.
Argomenti: var, record, text block, switch expression e pattern matching, sealed classes, lambda/method reference, Stream API (filter/map/collect/reduce), Optional, try-with-resources idiomatico, java.time.
Traccia: rifai l'esercizio Università con record invece di classe, Stream API per getStudentiByCognome, Optional per getStudente(matricola), var dove ha senso. Confronta le due versioni.
- Un argomento per sessione. Non mischiare.
- Leggi, poi scrivi. Minimo 15 minuti di lettura, minimo 1 ora di codice.
- Spiega a voce alta. Se non sai raccontare un concetto a un amico, non l'hai ancora capito.
- Diario di debugging. Ogni errore che risolvi, una riga su cosa era e come l'hai capito.
- Non copiare codice: riscrivilo. Chiudi la soluzione trovata e riproducila a memoria.
- Usa Git anche solo localmente: un commit per esercizio.
- Pause vere. Dopo 90 minuti, 15 minuti lontano dallo schermo.
- Documentazione API Java 21 — https://docs.oracle.com/en/java/javase/21/docs/api/
- Java Tutorial (Oracle) — https://docs.oracle.com/javase/tutorial/
- Baeldung — https://www.baeldung.com/
- Effective Java di Joshua Bloch. Da leggere dopo la Fase 11, prima della 12.
- Stampa lo stato con
System.out.printlnper ispezionare le variabili. - Leggi il messaggio d'errore una riga alla volta, dall'alto.
- Scrivi a parole cosa vuoi ottenere in quel punto esatto.
- Solo dopo aver fatto 1-3, cerca o chiedi.
- Se la soluzione funziona ma non capisci perché, fermati e indaga prima di andare avanti.