1. Tipi di eccezioni

Tipi di eccezioni

Tutte le eccezioni sono divise in 4 tipi, che in realtà sono classi che si ereditano a vicenda.

Throwableclasse

La classe base per tutte le eccezioni è la Throwableclasse. La Throwableclasse contiene il codice che scrive lo stack di chiamate corrente (traccia dello stack del metodo corrente) in un array. Impareremo cos'è una traccia dello stack un po' più tardi.

L' operatore throw può accettare solo un oggetto che deriva dalla Throwableclasse. E sebbene tu possa teoricamente scrivere codice come throw new Throwable();, di solito nessuno lo fa. Lo scopo principale della Throwableclasse è avere un'unica classe genitore per tutte le eccezioni.

Errorclasse

La prossima classe di eccezione è la Errorclasse, che eredita direttamente la Throwableclasse. La macchina Java crea oggetti della Errorclasse (e dei suoi discendenti) quando si sono verificati problemi seri . Ad esempio, un malfunzionamento dell'hardware, memoria insufficiente, ecc.

Di solito, come programmatore, non c'è niente che tu possa fare in una situazione in cui si è verificato un tale errore (il tipo per il quale Errordovrebbe essere lanciato un an): questi errori sono troppo gravi. Tutto quello che puoi fare è notificare all'utente che il programma si sta arrestando in modo anomalo e/o scrivere tutte le informazioni note sull'errore nel registro del programma.

Exceptionclasse

Le classi Exceptione RuntimeExceptionsono per errori comuni che si verificano nel funzionamento di molti metodi. L'obiettivo di ogni eccezione generata è essere catturato da un catchblocco che sappia come gestirlo correttamente.

Quando un metodo non può completare il proprio lavoro per qualche motivo, dovrebbe immediatamente avvisare il metodo chiamante generando un'eccezione del tipo appropriato.

In altre parole, se una variabile è uguale a null, il metodo genererà un NullPointerException. Se gli argomenti errati sono stati passati al metodo, genererà un file InvalidArgumentException. Se il metodo divide accidentalmente per zero, genererà un'estensione ArithmeticException.

RuntimeExceptionclasse

RuntimeExceptionssono un sottoinsieme di Exceptions. Potremmo anche dire che RuntimeExceptionè una versione leggera delle eccezioni ordinarie ( Exception) — meno requisiti e restrizioni sono imposti a tali eccezioni

Imparerai la differenza tra Exceptione RuntimeExceptiondopo.


2. Throws: eccezioni verificate

Genera: eccezioni verificate

Tutte le eccezioni Java rientrano in 2 categorie: verificate e non verificate .

Tutte le eccezioni che ereditano RuntimeExceptiono Errorsono considerate eccezioni non controllate . Tutti gli altri sono eccezioni controllate .

Importante!

Vent'anni dopo l'introduzione delle eccezioni verificate, quasi tutti i programmatori Java lo considerano un bug. Nei framework moderni più diffusi, il 95% di tutte le eccezioni è deselezionato. Il linguaggio C#, che quasi copiava esattamente Java, non aggiungeva eccezioni controllate .

Qual è la differenza principale tra eccezioni controllate e non controllate ?

Ci sono ulteriori requisiti imposti sulle eccezioni verificate . In parole povere sono questi:

Requisito 1

Se un metodo genera un'eccezione verificata , deve indicare il tipo di eccezione nella sua firma . In questo modo, ogni metodo che lo chiama è consapevole che questa "eccezione significativa" potrebbe verificarsi in esso.

Indica le eccezioni verificate dopo i parametri del metodo dopo la throwsparola chiave (non utilizzare la throwparola chiave per errore). Assomiglia a questo:

type method (parameters) throws exception

Esempio:

eccezione verificata eccezione non verificata
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

Nell'esempio a destra, il nostro codice genera un'eccezione non verificata : non è richiesta alcuna azione aggiuntiva. Nell'esempio a sinistra, il metodo genera un'eccezione verificata , quindi la throwsparola chiave viene aggiunta alla firma del metodo insieme al tipo di eccezione.

Se un metodo prevede di generare più eccezioni controllate , tutte devono essere specificate dopo la throwsparola chiave, separate da virgole. L'ordine non è importante. Esempio:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

Requisito 2

Se chiami un metodo che ha verificato le eccezioni nella sua firma, non puoi ignorare il fatto che le genera.

Devi rilevare tutte queste eccezioni aggiungendo catchblocchi per ognuna o aggiungendole a una throwsclausola per il tuo metodo.

È come se stessimo dicendo: " Queste eccezioni sono così importanti che dobbiamo rilevarle. E se non sappiamo come gestirle, allora chiunque chiami il nostro metodo deve essere avvisato che tali eccezioni possono verificarsi in esso.

Esempio:

Immagina di scrivere un metodo per creare un mondo popolato da umani. Il numero iniziale di persone viene passato come argomento. Quindi dobbiamo aggiungere eccezioni se ci sono troppo poche persone.

Creare la Terra Nota
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
Il metodo genera potenzialmente due eccezioni verificate :

  • EmptyWorldException
  • LonelyWorldException

Questa chiamata al metodo può essere gestita in 3 modi:

1. Non cogliere eccezioni

Questo è più spesso fatto quando il metodo non sa come gestire correttamente la situazione.

Codice Nota
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Il metodo chiamante non rileva le eccezioni e deve informarne gli altri: le aggiunge alla propria throwsclausola

2. Cattura alcune delle eccezioni

Gestiamo gli errori che possiamo gestire. Ma quelli che non capiamo, li lanciamo al metodo di chiamata. Per fare ciò, dobbiamo aggiungere il loro nome alla clausola throws:

Codice Nota
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
Il chiamante rileva solo un'eccezione verificata : LonelyWorldException. L'altra eccezione va aggiunta alla sua firma, indicandola dopo la throwsparola chiave

3. Cattura tutte le eccezioni

Se il metodo non genera eccezioni al metodo chiamante, il metodo chiamante è sempre sicuro che tutto abbia funzionato correttamente. E non sarà in grado di intraprendere alcuna azione per risolvere una situazione eccezionale.

Codice Nota
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Tutte le eccezioni vengono rilevate in questo metodo. Il chiamante sarà sicuro che tutto è andato bene.


3. Eccezioni di wrapping

Le eccezioni controllate sembravano interessanti in teoria, ma in pratica si sono rivelate un'enorme frustrazione.

Supponi di avere un metodo super popolare nel tuo progetto. Viene chiamato da centinaia di posti nel tuo programma. E decidi di aggiungervi una nuova eccezione verificata . E può darsi che questa eccezione verificata sia davvero importante e così speciale che solo il main()metodo sa cosa fare se viene rilevata.

Ciò significa che dovrai aggiungere l' eccezione verificata alla throwsclausola di ogni metodo che chiama il tuo metodo super popolare . Così come nella throwsclausola di tutti i metodi che chiamano quei metodi. E dei metodi che chiamano quei metodi.

Di conseguenza, le throwsclausole di metà dei metodi nel progetto ottengono una nuova eccezione verificata . E ovviamente il tuo progetto è coperto da test, e ora i test non vengono compilati. E ora devi anche modificare le clausole di lancio nei tuoi test.

E poi tutto il tuo codice (tutte le modifiche in centinaia di file) dovrà essere rivisto da altri programmatori. E a questo punto ci chiediamo perché abbiamo apportato tante maledette modifiche al progetto? Giorno (i?) Di lavoro e test interrotti, tutto per aggiungere un'eccezione verificata ?

E, naturalmente, ci sono ancora problemi relativi all'ereditarietà e all'override del metodo. I problemi che derivano dalle eccezioni verificate sono molto più grandi del vantaggio. La linea di fondo è che ora poche persone li amano e poche persone li usano.

Tuttavia c'è ancora molto codice (incluso il codice della libreria Java standard) che contiene queste eccezioni controllate . Cosa si deve fare con loro? Non possiamo ignorarli e non sappiamo come gestirli.

I programmatori Java hanno proposto di racchiudere le eccezioni verificate in RuntimeException. In altre parole, intercetta tutte le eccezioni verificate , quindi crea eccezioni non verificateRuntimeException (ad esempio, ) e lanciale invece. In questo modo assomiglia a questo:

try
{
   // Code where a checked exception might occur
}
catch(Exception exp)
{
   throw new RuntimeException(exp);
}

Non è una soluzione molto carina, ma qui non c'è nulla di criminale: l'eccezione è stata semplicemente inserita in un file RuntimeException.

Se lo desideri, puoi recuperarlo facilmente da lì. Esempio:

Codice Nota
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Ottenere l'eccezione memorizzata all'interno dell'oggetto RuntimeException. La causevariabile potrebbe null

determinarne il tipo e convertirla in un tipo di eccezione verificato .


4. Cattura di più eccezioni

I programmatori odiano davvero duplicare il codice. Hanno persino escogitato un principio di sviluppo corrispondente: Don't Repeat Yourself (DRY) . Ma quando si gestiscono le eccezioni, ci sono frequenti occasioni in cui un tryblocco è seguito da più catchblocchi con lo stesso codice.

Oppure potrebbero esserci 3 catchblocchi con lo stesso codice e altri 2 catchblocchi con altro codice identico. Questa è una situazione standard in cui il tuo progetto gestisce le eccezioni in modo responsabile.

A partire dalla versione 7, nel linguaggio Java è stata aggiunta la possibilità di specificare più tipi di eccezioni in un singolo catchblocco. Assomiglia a questo:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Puoi avere tutti catchi blocchi che vuoi. Tuttavia, un singolo catchblocco non può specificare eccezioni che si ereditano a vicenda. In altre parole, non puoi scrivere catch ( Exception| RuntimeExceptione), perché la RuntimeExceptionclasse eredita Exception.



5. Eccezioni personalizzate

Puoi sempre creare la tua classe di eccezione. Devi semplicemente creare una classe che erediti la RuntimeExceptionclasse. Sarà simile a questo:

class ClassName extends RuntimeException
{
}

Discuteremo i dettagli mentre impari OOP, ereditarietà, costruttori e override del metodo.

Tuttavia, anche se hai solo una classe semplice come questa (completamente senza codice), puoi comunque lanciare eccezioni basate su di essa:

Codice Nota
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Lancia un MyException .

Nella ricerca Java Multithreading , faremo un tuffo nel lavorare con le nostre eccezioni personalizzate.