1. Eccezioni

>

Alla fine, i programmatori hanno pensato di standardizzare e automatizzare la gestione degli errori. Questo è successo quando sono state inventate le eccezioni . Ora il meccanismo delle eccezioni gestisce l'80% delle situazioni eccezionali.

Se qualche studioso ha fatto delle eccezioni, è probabile che fosse l'argomento della sua tesi di dottorato. Se l'ha inventato un programmatore, potrebbe aver ricevuto un'amichevole pacca sulla spalla da un collega: "Sembra a posto, fratello".

Quando si verifica un errore in un programma Java, come la divisione per 0, accadono cose meravigliose:

Primo passo

Viene creato un oggetto eccezione speciale, che contiene informazioni sull'errore che si è verificato.

Tutto in Java è un oggetto e le eccezioni non sono eccezioni 🙂 Gli oggetti eccezione hanno le proprie classi e l'unica cosa che li distingue dalle classi ordinarie è che ereditano la Throwableclasse.

Passo due

L'oggetto eccezione è "lanciato". Forse la formulazione qui potrebbe essere migliore. "Lanciare un'eccezione" è più come attivare un allarme antincendio o emettere un avviso "DEFCON 1".

Quando viene lanciata un'eccezione alla macchina Java, il normale funzionamento del programma si interrompe e iniziano i "protocolli di emergenza".

Fase tre

Il metodo in cui è stata generata l'eccezione esce immediatamente. L'eccezione viene passata al metodo chiamante, che termina anch'esso immediatamente. E così via lungo la catena finché il mainmetodo non esce. Quando il mainmetodo termina, termina anche il programma.

Esempio:

Codice Uscita console
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Your attention, please! Preparing for the end of the world");
      endTheWorld();
      System.out.println("The world ended successfully");
   }

   public static void endTheWorld()
   {
      System.out.println("We're doing something important");
      doSomeWork(0);
      System.out.println("Everything is working well");
   }

   public static void doSomeWork(int n)
   {
      System.out.println("Nothing terrible will happen: " + n);
      System.out.println(2 / n);
      System.out.println("Nothing terrible happened: " + n);
   }
}
Your attention, please! Preparing for the end of the world
We're doing something important
Nothing terrible will happen: 0

Un'eccezione si verifica alla riga 20: divisione per 0. La macchina Java crea immediatamente un'eccezione — un ArithmeticExceptionoggetto e lo "lancia" al metodo.

Il divide()metodo termina immediatamente, quindi non vediamo mai la stringa: Non è successo niente di terribile: 0. Il programma ritorna al endTheWorld()metodo e la situazione si ripete: c'è un'eccezione non gestita nel sistema, il che significa che endTheWorld()anche il metodo termina in modo anomalo. Quindi il mainmetodo termina e il programma si arresta.

Qual è lo scopo di queste eccezioni? Bene, puoi scrivere il tuo codice per catturare particolari tipi di eccezioni e scrivere la tua logica per gestire situazioni eccezionali.


2. Eccezioni alla cattura:try-catch

Java ha un meccanismo di cattura delle eccezioni che ti consente di fermare questa terminazione anomala dei metodi. Sembra così:

try
{
   // Code where an exception might occur
}
catch(ExceptionType name)
{
   // Exception handling code
}

Questo costrutto è chiamato try-catchblocco.

Il codice in cui possono verificarsi eccezioni è racchiuso tra parentesi graffe, preceduto dalla parola try.

Dopo le parentesi graffe abbiamo la catchparola chiave e, tra parentesi, la dichiarazione di una variabile di eccezione . Questo è seguito da parentesi graffe che racchiudono il codice da eseguire se si verifica un'eccezione del tipo specificato .

Se non vengono lanciate eccezioni durante l'esecuzione del " codice primario ", il codice all'interno del blocco catch non verrà eseguito. Se si verifica un'eccezione, lo sarà (se il tipo dell'eccezione generata è uguale al tipo della variabile tra parentesi).

Esempio:

Codice Uscita console
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Hadron Collider launched");

      try
      {
         launchHadronCollider(1);
         launchHadronCollider(0);
      }
      catch(Exception e)
      {
         System.out.println("Error! Caught an exception");
         System.out.println("The planet was sucked into a black hole!");
      }

      System.out.println("The Hadron Collider stopped");
   }

   public static void launchHadronCollider(int n)
   {
      System.out.println("Everything is working well: " + n);
      System.out.println(2/n);
      System.out.println("There are no problems: " + n);
   }
}
Hadron Collider launched
Everything is working fine: 1
2
There are no problems: 1
Everything is working fine: 0
Error! Caught an exception
The planet has been sucked into a black hole!
The Hadron Collider is stopped


3. Più catchblocchi

Più blocchi di cattura

In teoria, tutti i tipi di eccezioni possono essere lanciati in un blocco di codice. Alcuni vorrai gestirli in un modo, altri in un altro, e altri ancora deciderai di non gestirli affatto.

Gli sviluppatori Java hanno deciso di aiutarti e di lasciarti scrivere non uno ma molti catchblocchi dopo il tryblocco.

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 name1)
{
   // Code for handling ExceptionType1
}
catch (ExceptionType2 name2)
{
   // Code for handling ExceptionType2
}
   catch (ExceptionType3 name3)
{
   // Code for handling ExceptionType3
}

Esempio:

Codice Uscita console
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Start of main method");
      try
      {
         calculate(0);
      }
      catch (ArithmeticException e)
      {
         System.out.println("Division by 0");
      }
      catch(Exception e)
      {
         System.out.println("Caught some kind of exception");
      }

      System.out.println("End of main method");
   }

   public static void calculate(int n)
   {
      System.out.println("Start of calculate method: " + n);
      System.out.println(2/n);
      System.out.println("End of calculate method: " + n);
   }
}
Start of main method
Start of calculate method: 0
Division by 0
End of main method


4. Ordine dei catchblocchi

Le eccezioni che si verificano in un tryblocco possono essere rilevate solo da un singolo catchblocco. Non è possibile avere una situazione di gestione delle eccezionicatch in cui viene eseguito il codice di più blocchi.

Ma l'ordine dei blocchi è importante.

Puoi avere una situazione in cui un'eccezione potrebbe essere rilevata da più blocchi. In tal caso, l'eccezione verrà rilevata dal blocco catch che viene prima (il più vicino al tryblocco).

Come puoi avere una situazione in cui più blocchi catch possono catturare la stessa eccezione?

Tutte le eccezioni appartengono a un'unica gerarchia di ereditarietà: vedere il diagramma.

Gerarchia delle eccezioni

Un ArithmeticExceptionoggetto può essere assegnato a una variabile il cui tipo è ArithmeticExceptiono una qualsiasi delle sue classi antenate: RuntimeException , Exceptione Throwable— vedi il diagramma.

Parleremo di più sull'ereditarietà e sulle classi ancestrali nel Livello 21.

Questo codice verrà compilato correttamente:

Vantaggi dell'eredità:
ArithmeticException ae    = new ArithmeticException();
RuntimeException runtime  = new ArithmeticException();
Exception exception       = new ArithmeticException();
Throwable trwbl           = new ArithmeticException();

Quindi puoi prendere un ArithmeticExceptioncon uno qualsiasi dei 4 catchblocchi sopra.

Esempio 1:

Codice Uscita console
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Start of main method");
      try
      {
         calculate(0);
      }
      catch(ArithmeticException e)
      {
         System.out.println("Division by 0");
      }
      catch(Exception e)
      {
         System.out.println("Caught some kind of exception");
      }

      System.out.println("End of main method");
   }

   public static void calculate(int n)
   {
      System.out.println("Start of calculate method: " + n);
      System.out.println(2/n);
      System.out.println("End of calculate method: " + n);
   }
}
Start of main method
Start of calculate method: 0
Division by 0
End of main method

In questo esempio, il ArithmeticExceptionpuò essere catturato da entrambi i blocchi catch (Exception e)e catch (ArithmeticException e). Sarà catturato dal blocco più vicino al tryblocco, il primo catchblocco.

Per evitare sorprese, è meglio posizionare catchi blocchi in grado di rilevare quasi tutte le eccezioni vicino alla fine dell'elenco dei catchblocchi.

Il Throwabletipo è generalmente in grado di rilevare ogni possibile eccezione in Java . Se lo metti nel primo catchblocco, il codice non verrà compilato, poiché il compilatore sa che ci sono blocchi di codice irraggiungibili.