Eccezioni in Java: come gestirle correttamente

Oggi vediamo le eccezioni in Java, ma non spiegherò cosa sono (ci sono miliardi di documenti sul web al riguardo), piuttosto farò un po’ di chiarezza su quali tipi di eccezioni esistono e su come gestirle correttamente.

Brevemente, in Java un’eccezione è un evento che si può presentare durante l’esecuzione di un programma, e che interrompe il normale flusso delle istruzioni. A livello pratico, un’eccezione è un oggetto che viene creato al presentarsi dell’errore, e la gerarchia delle classi è visibile nella seguente figura, e ha come padre Throwable. […]

Oggi vediamo le eccezioni in Java, ma non spiegherò cosa sono (ci sono miliardi di documenti sul web al riguardo), piuttosto farò un po’ di chiarezza su quali tipi di eccezioni esistono e su come gestirle correttamente.

Brevemente, in Java un’eccezione è un evento che si può presentare durante l’esecuzione di un programma, e che interrompe il normale flusso delle istruzioni. A livello pratico, un’eccezione è un oggetto che viene creato al presentarsi dell’errore, e la gerarchia delle classi è visibile nella seguente figura, e ha come padre Throwable.

eccezioni_java
Gerarchia delle eccezioni Java

Ci sono 2 classi distinte: Error e Exception. A sua volta, la classe Exception può essere suddivisa in questo modo:

  • eccezioni controllate (checked): possono verificarsi indipendentemente dalle scelte del programmatore, per esempio un errore durante la lettura di un file.
  • eccezioni non controllate (unchecked): classi RuntimeException e Error. Possono verificarsi indipendentemente dalle scelte del client/utente, e possono invece dipendere dalla realizzazione di codice poco robusto, come ad esempio eccezioni NullPointerException (colpa del programmatore).

Questa distinzione è molto importante, perché ci dice quali sono le eccezioni da prendere in considerazione durante la scrittura di un programma e dunque quali sono le eccezioni da gestire. Come suggerisce giustamente il nome :-), le eccezioni controllate devono necessariamente essere gestite all’interno di un programma, con i canonici comandi try/catch, altrimenti il compilatore non sarà in grado di compilare con successo il codice. Le eccezioni controllate possono ad esempio essere generate con l’apertura di un file o di una risorsa (IOException), oppure con l’acceso o manipolazione di un database (SQLException), e le cause sono esterne al programma stesso. Per questo motivo il programmatore deve gestire adeguatamente le eccezioni di questo tipo, non sapendo se saranno sollevate (la risorsa può non essere disponibile, la connessione al database può essere stata interrotta, …). Un semplice codice di esempio può essere il seguente:

try {
//apertura file
}catch (FileNotFoundException fnfe)
{
System.err.println("FileNotFoundException: " + fnfe.getMessage());
}

Le eccezioni non controllate invece non devono obbligatoriamente essere gestite, in quanto non dovrebbero sollevarsi in presenza di codice ben scritto (tutti noi scriviamo codice robusto no?! ;-)). In ogni caso è bene controllare attentamente il codice scritto al fine di evitare brutte sorprese ed evitare assolutamente eccezioni del tipo: un puntatore è nullo (NullPointerException), è state effettuata una divisione di un numero per zero (ArithmeticException), … Ad esempio un codice come il seguente:

try
{
int x;//input fornito dall'utente
System.out.println(5/x);
}catch(ArithmeticException ae)
{
System.err.println(ae.getMessage());
}

dovrebbe essere sostituito con il seguente codice:

int x;//input fornito dall'utente
if(x == 0)
{
System.out.println("Errore: l'input fornito è errato. Non è accettato il valore 0.");
System.exit(1);
}else
{
System.out.println(5/x);
}

Per ulteriori approfondimenti sulle eccezioni in generale leggere:

roghan

Privilegi root per bind di porte 1-1023 in Java

Se dobbiamo creare un server in Java per accettare comunicazioni su TCP, è necessario usare la classe ServerSocket, mentre quella Socket è specifica per il lato client. Durante la creazione della componente client, bisogna fare particolare attenzione a quali porte indichiamo se ci troviamo su sistemi Linux/Unix. […]

Se dobbiamo creare un server in Java per accettare comunicazioni su TCP, è necessario usare la classe ServerSocket, mentre quella Socket è specifica per il lato client. Durante la creazione della componente client, bisogna fare particolare attenzione a quali porte indichiamo se ci troviamo su sistemi Linux/Unix. Ad esempio, il codice per mettere in ascolto un server sulla porta 80 è il seguente:

try
{
     ServerSocket server= new ServerSocket(80);
}catch (IOException iex)
{
     System.err.println(ex);
}

Il codice è corretto, ma potrebbe essere sollevata un’eccezione su sistemi Unix/Linux o anche Windows, in quanto sono necessari i privilegi di root/administrator per poter usare le porte nel range 1-1023.

roghan