1. Classe Throwable: la radice di tutte le eccezioni
Ora analizziamo in dettaglio come è strutturato il sistema delle eccezioni in Java: che cos’è Throwable, in cosa differiscono Exception ed Error, e che cosa significano eccezioni «controllate» e «non controllate». È il fondamento per una corretta gestione degli errori nelle vostre applicazioni.
In Java tutte le eccezioni e gli errori sono oggetti che derivano dalla classe java.lang.Throwable.
Throwable è il «capostipite» dell’intera gerarchia per la gestione dei problemi in Java.
Schematicamente:
Throwable
├── Exception
└── Error
Throwable è la classe base per tutto ciò che può essere «lanciato» (throw) e «catturato» (catch) in Java. Non si usa direttamente: funge da base per tipi di errore più specifici.
Exception – per errori «normali»
Exception è la classe base per tutte le eccezioni che possono verificarsi in un programma e che si possono e si devono gestire. Sono errori «operativi»: problemi con file, rete, I/O, input dell’utente, ecc. La maggior parte dei vostri try-catch lavorerà proprio con i discendenti di Exception.
Esempi:
- IOException – errore durante l’accesso a file o rete.
- SQLException – errore durante il lavoro con il database.
- FileNotFoundException – file non trovato.
Error – per errori fatali della JVM
Error è la classe base per errori che avvengono a livello di Java Virtual Machine (JVM). Di solito sono guasti critici che il programma non può e non dovrebbe gestire. Se si verifica un Error, molto probabilmente l’applicazione non potrà proseguire.
Esempi:
- OutOfMemoryError – memoria esaurita.
- StackOverflowError – overflow dello stack (ad esempio a causa di ricorsione infinita).
- NoClassDefFoundError – classe necessaria non trovata.
Importante:
Catturare e gestire Error è quasi sempre una cattiva idea. Non sono errori del vostro programma, ma guasti dell’ambiente di esecuzione.
2. Checked vs unchecked exceptions: che cosa significa?
In Java tutte le eccezioni si dividono in due grandi gruppi:
Eccezioni controllate (Checked)
Che cosa sono? Eccezioni che il compilatore impone di gestire o di propagare esplicitamente.
Quando si verificano? Di solito sono legate a risorse esterne: file, rete, database, input dell’utente.
Come gestirle? Occorre racchiudere il codice in try-catch oppure aggiungere throws alla firma del metodo.
Esempi: IOException, SQLException, FileNotFoundException
Esempio:
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // può generare IOException
// ...
}
Se non le gestisci o non le propaghi, il programma non verrà compilato!
Eccezioni non controllate (Unchecked)
Che cosa sono? Eccezioni che non richiedono gestione obbligatoria da parte del compilatore.
Quando si verificano? Di solito sono errori nella logica del programma: divisione per zero, uscita dai limiti dell’array, accesso a null.
Come gestirle? Si possono catturare, ma non è obbligatorio. È meglio prevenire tali errori con opportune verifiche.
Dove nella gerarchia? Tutte discendono da RuntimeException.
Esempi: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, ArithmeticException
Esempio:
int[] arr = {1, 2, 3};
System.out.println(arr[10]); // ArrayIndexOutOfBoundsException
Il compilatore non obbliga a catturare questa eccezione, ma l’applicazione si arresterà in caso di errore.
3. L’intera gerarchia in un’unica figura
graph TD
Throwable --> Error
Throwable --> Exception
Exception --> RuntimeException
Exception --> CheckedExceptions["(altre eccezioni checked)"]
Error --> OutOfMemoryError
Error --> StackOverflowError
RuntimeException --> NullPointerException
RuntimeException --> IndexOutOfBoundsException
RuntimeException --> IllegalArgumentException
%% Stili
style Throwable fill:#ffa64d,color:#000
style Exception fill:#ffa64d,color:#000
style CheckedExceptions fill:#ffa64d,color:#000
style Error fill:#ff4d4d,color:#fff
style OutOfMemoryError fill:#ff4d4d,color:#fff
style StackOverflowError fill:#ff4d4d,color:#fff
style RuntimeException fill:#4dff88,color:#000
style NullPointerException fill:#4dff88,color:#000
style IndexOutOfBoundsException fill:#4dff88,color:#000
style IllegalArgumentException fill:#4dff88,color:#000
Tabella: differenze principali
| Gruppo | Classe base | Richiede gestione? | Esempi |
|---|---|---|---|
| Checked Exception | |
Sì | |
| Unchecked | |
No | |
| Error | |
No | |
4. Come appare nel codice?
Checked exception: esempio con i file
import java.io.*;
public class FileDemo {
public static void main(String[] args) {
try {
FileReader reader = new FileReader("nofile.txt"); // FileNotFoundException (checked)
int data = reader.read();
System.out.println(data);
reader.close();
} catch (IOException e) {
System.out.println("Errore durante l'operazione sul file: " + e.getMessage());
}
}
}
Il compilatore ti obbligherà a gestire IOException!
Unchecked exception: esempio con divisione per zero
public class ExceptionDemo {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a / b; // ArithmeticException (unchecked)
System.out.println("Risultato: " + c);
}
}
Il compilatore non richiede la gestione, ma il programma si arresterà.
5. Perché serve la gerarchia delle eccezioni?
- Flessibilità di gestione: È possibile catturare sia errori specifici (FileNotFoundException) sia intere famiglie (IOException o Exception).
- Riutilizzo del codice: Si possono gestire centralmente errori dello stesso tipo.
- Pulizia del codice: La logica principale non viene appesantita da controlli per ogni minimo dettaglio.
Esempio:
try {
// codice rischioso
} catch (FileNotFoundException e) {
System.out.println("File non trovato!");
} catch (IOException e) {
System.out.println("Errore di I/O!");
} catch (Exception e) {
System.out.println("Qualcosa è andato storto: " + e.getMessage());
}
6. Errori tipici nella gestione delle eccezioni
Errore n. 1: ignorare le eccezioni. Scrivere catch (Exception e) {} – è una cattiva pratica! Perdi informazioni sulla causa del problema.
Errore n. 2: si cattura troppo. catch (Exception e) cattura tutto, anche ciò che non ti aspetti. Meglio catturare solo le eccezioni che sai gestire.
Errore n. 3: si catturano gli Error. Non dovresti catturare Error, a meno che tu non stia scrivendo codice di basso livello. Sono problemi della JVM, non della tua applicazione.
Errore n. 4: non distinguere tra checked e unchecked. Non tutte le eccezioni sono uguali! Le checked richiedono gestione (Exception), le unchecked no (RuntimeException e discendenti).
Errore n. 5: non aggiungere informazioni alle eccezioni. Se crei eccezioni personalizzate, aggiungi sempre un messaggio informativo.
GO TO FULL VERSION