19. Eccezioni e Gestione degli Errori
Indice
- 19.1 Gerarchia e tipi di eccezioni
- 19.2 Dichiarare e lanciare eccezioni
- 19.3 Override dei metodi e regole sulle eccezioni
- 19.4 Gestione delle eccezioni: try, catch, finally
- 19.5 Gestione automatica delle risorse try-with-resources
- 19.6 Eccezioni soppresse
- 19.7 Riepilogo sulle eccezioni
Le Exceptions sono il meccanismo strutturato di Java per gestire condizioni anomale a runtime.
Permettono ai programmi di separare il flusso di esecuzione normale dalla logica di gestione degli errori, migliorando robustezza, leggibilità e correttezza.
19.1 Gerarchia e tipi di eccezioni
Tutte le eccezioni derivano da Throwable.
La gerarchia definisce quali condizioni sono recuperabili, quali devono essere dichiarate e quali rappresentano errori fatali del sistema.
java.lang.Object
└── java.lang.Throwable
├── java.lang.Error
└── java.lang.Exception
└── java.lang.RuntimeException
19.1.1 Throwable
- Classe base per tutti gli errori e le eccezioni
- Supporta messaggio, causa e stack trace
- Solo
Throwable(e le sue sottoclassi) può essere lanciato o catturato
19.1.2 Error (unchecked)
- Rappresenta gravi problemi della JVM o del sistema
- Non è pensato per essere catturato o gestito
- Esempi:
OutOfMemoryError,StackOverflowError
Note
- Gli Error indicano condizioni dalle quali l’applicazione generalmente non è attesa a riprendersi.
19.1.3 Eccezioni Checked (Exception)
- Sottoclassi di
ExceptionescludendoRuntimeException - Rappresentano condizioni che le applicazioni potrebbero voler gestire
- Devono essere catturate oppure dichiarate
- Esempi:
IOException,SQLException
19.1.4 Eccezioni Unchecked (RuntimeException)
- Sottoclassi di
RuntimeException - Non è richiesto dichiararle o catturarle
- Solitamente rappresentano errori di programmazione
- Esempi:
NullPointerException,IllegalArgumentException
19.2 Dichiarare e lanciare eccezioni
19.2.1 Dichiarare eccezioni con throws
Un metodo dichiara eccezioni checked usando la clausola throws. Questa fa parte del contratto API del metodo.
void readFile(Path p) throws IOException {
Files.readString(p);
}
Note
- Solo le eccezioni checked devono essere dichiarate.
- Le eccezioni unchecked possono essere dichiarate, ma di solito vengono omesse.
19.2.2 Lanciare eccezioni
Le eccezioni vengono create con new e lanciate esplicitamente usando throw.
if (value < 0) {
throw new IllegalArgumentException("value must be >= 0");
}
throwlancia esattamente un’istanza di eccezionethrowsdichiara le possibili eccezioni nella firma del metodo
19.3 Override dei metodi e regole sulle eccezioni
Quando si fa override di un metodo, le regole sulle eccezioni sono applicate in modo rigoroso.
- Un metodo in override può lanciare meno o più specifiche eccezioni checked
- Può lanciare qualsiasi eccezione unchecked
- Non può lanciare nuove o più generiche eccezioni checked
class Parent {
void work() throws IOException {}
}
class Child extends Parent {
@Override
void work() throws FileNotFoundException {} // OK (sottoclasse)
}
Note
- Cambiare solo le eccezioni unchecked non rompe mai il contratto di override.
Important
Ricorda: i costruttori seguono una regola diversa.
Un Costruttore deve dichiarare tutte le eccezioni checked dichiarate nel costruttore di base (o le superclassi di quelle eccezioni checked).
Può anche dichiarare eccezioni checked aggiuntive. Questo comportamento è l’opposto di quello dell’overriding dei metodi.
Un metodo che override non può lanciare alcuna eccezione checked diversa da quelle dichiarate dal metodo overridato. Può lanciare solo sottoclassi di tali eccezioni.
19.4 Gestione delle eccezioni: try, catch, finally
19.4.1 Sintassi base try-catch
try {
riskyOperation();
} catch (IOException e) {
handle(e);
}
- Un blocco
trydeve essere seguito da almeno uncatchoppure da unfinally - I catch vengono controllati dall’alto verso il basso
19.4.2 Blocchi catch multipli
Blocchi catch multipli permettono gestioni diverse per tipi di eccezione differenti.
try {
process();
} catch (FileNotFoundException e) {
recover();
} catch (IOException e) {
log();
}
Note
- Le eccezioni più specifiche devono venire prima di quelle più generali, altrimenti la compilazione fallisce.
- Se si mette un catch per una superclasse (es.
IOException) prima di uno per una sottoclasse (es.FileNotFoundException), il catch della sottoclasse diventa irraggiungibile.
19.4.3 Multi-catch (Java 7+)
try {
process();
} catch (IOException | SQLException e) {
log(e);
}
- I tipi di eccezione devono essere non correlati (nessuna relazione parent/child)
- La variabile catturata è implicitamente
final
19.4.4 Blocco finally
Il blocco finally viene eseguito indipendentemente dal fatto che venga lanciata un’eccezione, tranne in casi estremi di terminazione della JVM.
try {
open();
} finally {
close();
}
- Usato per logica di cleanup
- Viene eseguito anche se si usa
returnnel blocco try e/o catch
Note
- Un blocco
finallypuò sovrascrivere un valore di ritorno o “inghiottire” un’eccezione. Questo è generalmente sconsigliato perché rende il flusso di controllo più difficile da comprendere.
Important
-
Quando sia un blocco
catchsia un bloccofinallylanciano un’eccezione, l’eccezione lanciata nel bloccofinallyè quella che viene propagata dal metodo. -
L’eccezione lanciata nel blocco
catchviene persa e non viene aggiunta alla lista delle eccezioni soppresse.
try {
throw new RuntimeException("try");
} catch (RuntimeException e) {
throw new RuntimeException("catch");
} finally {
throw new RuntimeException("finally");
}
In questo caso, viene lanciata solo l’eccezione "finally".
19.5 Gestione automatica delle risorse (try-with-resources)
Il try-with-resources fornisce la chiusura automatica delle risorse che implementano AutoCloseable.
Elimina la necessità di cleanup esplicito con finally nella maggior parte dei casi.
19.5.1 Sintassi base
try (BufferedReader br = Files.newBufferedReader(path)) {
return br.readLine();
}
- Le risorse vengono chiuse automaticamente
- La chiusura avviene anche se viene lanciata un’eccezione
- Le risorse vengono chiuse prima dell'esecuzione dei blocchi
catcho delfinally
try (Resource a = new Resource()) {
a.read();
} finally {
a.close(); // ❌ Compile-time error: a è out of scope qui
}
19.5.2 Dichiarare risorse multiple
try (InputStream in = Files.newInputStream(p);
OutputStream out = Files.newOutputStream(q)) {
in.transferTo(out);
}
- Le risorse vengono chiuse in ordine inverso rispetto alla dichiarazione
19.5.3 Scope delle risorse
- Le risorse sono visibili solo all’interno del blocco
try - Sono implicitamente
final - Da Java 9, si possono dichiarare risorse in anticipo, fuori dal try-with-resources, purché siano dichiarate
finalo siano effettivamente final.
final var firstWriter = Files.newBufferedWriter(filePath);
try (firstWriter; var secondWriter = Files.newBufferedWriter(filePath)) {
// CODE
}
Note
- Tentare di riassegnare una variabile risorsa causa un errore di compilazione.
Resource a = new Resource();
try(a){ // since Java 9
...
}finally{
a.close(); // questo codice compila ma la risorsa puntata dal reference 'a', è stata chiusa.
}
19.6 Eccezioni soppresse
Quando sia il blocco try sia il metodo close() della risorsa lanciano eccezioni, Java conserva l’eccezione principale e sopprime le altre.
try (BadResource r = new BadResource()) {
throw new RuntimeException("main");
}
Se anche close() lancia un’eccezione, questa diventa soppressa.
catch (Exception e) {
for (Throwable t : e.getSuppressed()) {
System.out.println(t);
}
}
- L’eccezione principale viene lanciata
- Le eccezioni secondarie sono accessibili tramite
getSuppressed()
Important
-
Le eccezioni soppresse vengono generate solo dal blocco
finallyimplicito creato daltry-with-resources. -
Al contrario, le eccezioni lanciate in un blocco
finallyesplicito non vengono soppresse: sostituiscono qualsiasi eccezione precedente e diventano l’unica eccezione propagata.
19.7 Riepilogo sulle eccezioni
- Le eccezioni checked devono essere catturate o dichiarate
- I metodi in override non possono ampliare le eccezioni checked
- Usa il multi-catch per logica di gestione condivisa
- Preferisci try-with-resources al cleanup con finally
- Le risorse si chiudono in ordine inverso
- Le eccezioni soppresse preservano il contesto completo del fallimento