CodeGym /Java Blog /Random-IT /Eccezioni: cattura e manipolazione
John Squirrels
Livello 41
San Francisco

Eccezioni: cattura e manipolazione

Pubblicato nel gruppo Random-IT
CIAO! Odio menzionarlo, ma gran parte del lavoro di un programmatore riguarda gli errori. Molto spesso, il suo. Si scopre che non ci sono persone che non commettono errori. E non ci sono nemmeno programmi del genere. Eccezioni: cattura e manipolazione - 1 Naturalmente, quando si ha a che fare con un errore, la cosa principale è capirne la causa. E molte cose possono causare bug in un programma. Ad un certo punto, i creatori di Java si sono chiesti cosa si dovrebbe fare con gli errori di programmazione più probabili? Evitarli del tutto non è realistico, i programmatori sono in grado di scrivere cose che non puoi nemmeno immaginare. :) Quindi, dobbiamo dare al linguaggio un meccanismo per lavorare con gli errori. In altre parole, se c'è un errore nel tuo programma, hai bisogno di una sorta di script per cosa fare dopo. Cosa dovrebbe fare esattamente un programma quando si verifica un errore? Oggi faremo conoscenza con questo meccanismo. Si chiama " eccezioni in Java ".

Cos'è un'eccezione?

Un'eccezione è una situazione eccezionale e non pianificata che si verifica mentre un programma è in esecuzione. Ci sono molte eccezioni. Ad esempio, hai scritto un codice che legge il testo da un file e visualizza la prima riga.

public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
Ma cosa succede se non esiste un file del genere! Il programma genererà un'eccezione: FileNotFoundException. Output: Eccezione nel thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Il sistema non riesce a trovare il percorso specificato) In Java, ogni eccezione è rappresentata da una classe separata. Tutte queste classi di eccezione derivano da un "antenato" comune: la Throwableclasse genitore. Il nome di una classe di eccezione di solito riflette in modo conciso il motivo per cui si è verificata l'eccezione:
  • FileNotFoundException(il file non è stato trovato)

  • ArithmeticException(si è verificata un'eccezione durante l'esecuzione di un'operazione matematica)

  • ArrayIndexOutOfBoundsException(l'indice è oltre i limiti dell'array). Ad esempio, questa eccezione si verifica se si tenta di visualizzare la posizione 23 di una matrice che contiene solo 10 elementi.
In tutto, Java ha quasi 400 classi di questo tipo! Perchè così tanti? Per renderli più convenienti per i programmatori con cui lavorare. Immagina questo: scrivi un programma e mentre viene eseguito genera un'eccezione simile a questa:

Exception in thread "main"
Uhhh. :/ Questo non aiuta molto. Non è chiaro cosa significhi l'errore o da dove provenga. Non ci sono informazioni utili qui. Ma la grande varietà di classi di eccezione in Java fornisce al programmatore ciò che conta di più: il tipo di errore e la sua probabile causa (incorporata nel nome della classe). È tutta un'altra cosa da vedere

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
È subito chiaro quale potrebbe essere il problema e da dove iniziare a scavare per risolverlo! Le eccezioni, come le istanze di qualsiasi classe, sono oggetti.

Catturare e gestire le eccezioni

Java ha blocchi di codice speciali per lavorare con le eccezioni: try, catche finally. Eccezioni: cattura e manipolazione - 2 Il codice in cui il programmatore ritiene che possa verificarsi un'eccezione viene inserito nel tryblocco. Ciò non significa che qui si verificherà un'eccezione. Significa che potrebbe verificarsi qui e il programmatore è a conoscenza di questa possibilità. Il tipo di errore che si prevede si verifichi viene inserito nel catchblocco. Questo contiene anche tutto il codice che dovrebbe essere eseguito se si verifica un'eccezione. Ecco un esempio:

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
Uscita: Errore! File non trovato! Mettiamo il nostro codice in due blocchi. Nel primo blocco, anticipiamo che potrebbe verificarsi un errore "File non trovato". Questo è il tryblocco. Nel secondo, diciamo al programma cosa fare se si verifica un errore. E il tipo di errore specifico: FileNotFoundException. Se inseriamo una classe di eccezione diversa tra parentesi del catchblocco, allora FileNotFoundExceptionnon verrà rilevata.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Output: Eccezione nel thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Il sistema non riesce a trovare il percorso specificato) Il codice nel catchblocco non è stato eseguito, perché abbiamo "configurato" questo blocco per catturare ArithmeticExceptione il codice nel tryblocco ha generato un tipo diverso: FileNotFoundException. Non abbiamo scritto alcun codice per gestire FileNotFoundException, quindi il programma visualizza le informazioni predefinite per FileNotFoundException. Qui devi prestare attenzione a tre cose. Numero uno. Una volta che si verifica un'eccezione su una riga del tryblocco, il codice che segue non verrà eseguito. L'esecuzione del programma "salta" immediatamente al catchblocco. Per esempio:

public static void main(String[] args) {
   try {
       System.out.println("Divide by zero");
       System.out.println(366/0);// This line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Output: Dividi per zero Il programma è saltato al blocco catch! Errore! Non puoi dividere per zero! Sulla seconda riga del tryblocco, tentiamo di dividere per 0, ottenendo un ArithmeticException. Di conseguenza, le righe 3-9 del tryblocco non verranno eseguite. Come abbiamo detto, il programma inizia immediatamente l'esecuzione del catchblocco. Numero due. Ci possono essere diversi catchblocchi. Se il codice nel tryblocco potrebbe generare non uno, ma diversi tipi di eccezioni, puoi scrivere un catchblocco per ciascuno di essi.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
      
       System.out.println("Error! File not found!");
      
   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");
      
   }
}
In questo esempio, abbiamo scritto due catchblocchi. Se a FileNotFoundExceptionsi verifica nel tryblocco, catchverrà eseguito il primo blocco. Se si verifica an ArithmeticException, verrà eseguito il secondo blocco. Potresti scrivere 50 catchblocchi se lo volessi. Naturalmente, è meglio non scrivere codice che potrebbe generare 50 diversi tipi di eccezioni. :) Terzo. Come fai a sapere quali eccezioni potrebbe generare il tuo codice? Bene, potresti essere in grado di indovinarne alcuni, ma è impossibile per te tenere tutto nella tua testa. Il compilatore Java conosce quindi le eccezioni più comuni e le situazioni in cui potrebbero verificarsi. Ad esempio, se scrivi codice che il compilatore sa che potrebbe generare due tipi di eccezioni, il tuo codice non verrà compilato finché non li gestirai. Vedremo esempi di questo sotto. Ora alcune parole sulla gestione delle eccezioni. Ci sono 2 modi per gestire le eccezioni. Abbiamo già incontrato il primo: il metodo può gestire l'eccezione stessa in un catch()blocco. Esiste una seconda opzione: il metodo può rilanciare l'eccezione nello stack di chiamate. Che cosa significa? Ad esempio, abbiamo una classe con lo stesso printFirstString()metodo, che legge un file e ne visualizza la prima riga:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Al momento, il nostro codice non viene compilato perché presenta eccezioni non gestite. Nella riga 1 si specifica il percorso del file. Il compilatore sa che tale codice potrebbe facilmente produrre un file FileNotFoundException. Nella riga 3, leggi il testo dal file. Questo processo potrebbe facilmente causare un IOException(errore di input/output). Ora il compilatore ti dice: "Amico, non approverò questo codice e non lo compilerò finché non mi dirai cosa dovrei fare se si verificasse una di queste eccezioni. E potrebbero certamente verificarsi in base al codice che hai scritto !" Non c'è modo di aggirarlo: devi gestirli entrambi! Conosciamo già il primo metodo di gestione delle eccezioni: dobbiamo inserire il nostro codice in un tryblocco e aggiungere due catchblocchi:

public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("File input/output error!");
       e.printStackTrace();
   }
}
Ma questa non è l'unica opzione. Potremmo semplicemente lanciare l'eccezione più in alto invece di scrivere codice di gestione degli errori all'interno del metodo. Questo viene fatto usando la parola chiave throwsnella dichiarazione del metodo:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Dopo la parola chiave throws, indichiamo un elenco separato da virgole di tutti i tipi di eccezioni che il metodo potrebbe generare. Perché? Ora, se qualcuno vuole chiamare il printFirstString()metodo nel programma, lui o lei (non tu) dovrà implementare la gestione delle eccezioni. Ad esempio, supponiamo che in un'altra parte del programma uno dei tuoi colleghi abbia scritto un metodo che chiama il tuo printFirstString()metodo:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
Abbiamo un errore! Questo codice non verrà compilato! Non abbiamo scritto codice per la gestione delle eccezioni nel printFirstString()metodo. Di conseguenza, questo compito ora ricade sulle spalle di coloro che utilizzano il metodo. In altre parole, il methodWrittenByYourColleague()metodo ora ha le stesse 2 opzioni: deve usare un try-catchblocco per gestire entrambe le eccezioni o lanciarle di nuovo.

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
Nel secondo caso, il metodo successivo nello stack di chiamate, quello che chiama methodWrittenByYourColleague(), dovrà gestire le eccezioni. Ecco perché lo chiamiamo "lanciare o far passare l'eccezione". Se lanci eccezioni verso l'alto usando la parola chiave throws, il tuo codice verrà compilato. A questo punto, il compilatore sembra dire: "Va bene, va bene. Il tuo codice contiene una serie di potenziali eccezioni, ma lo compilerò. Ma torneremo a questa conversazione!" E quando chiami un metodo che ha eccezioni non gestite, il compilatore mantiene la sua promessa e te le ricorda di nuovo. Parleremo infine del finallyblocco (scusate il gioco di parole). Questa è l'ultima parte del try-catch-finallytriumvirato che gestisce le eccezioni..

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
   }
}
In questo esempio, il codice all'interno del finallyblocco verrà eseguito in entrambi i casi. Se il codice nel tryblocco viene eseguito completamente senza generare eccezioni, il finallyblocco verrà eseguito alla fine. Se il codice all'interno del tryblocco viene interrotto da un'eccezione e il programma salta al catchblocco, il finallyblocco verrà comunque eseguito dopo il codice all'interno del catchblocco. Perché è necessario? Il suo scopo principale è eseguire codice obbligatorio: codice che deve essere eseguito indipendentemente dalle circostanze. Ad esempio, spesso libera alcune risorse utilizzate dal programma. Nel nostro codice, apriamo uno stream per leggere le informazioni dal file e passarle all'oggetto BufferedReader. Dobbiamo chiudere il nostro lettore e rilasciare le risorse. Questo deve essere fatto in ogni caso, quando il programma funziona come dovrebbe e quando genera un'eccezione. Il finallyblocco è un posto molto comodo per fare questo:

public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Ora siamo certi che ci occuperemo delle risorse, indipendentemente da ciò che accade quando il programma è in esecuzione. :) Non è tutto ciò che devi sapere sulle eccezioni. La gestione degli errori è un argomento estremamente importante nella programmazione. Ad esso sono dedicati molti articoli. Nella prossima lezione scopriremo quali tipi di eccezioni esistono e come creare le tue eccezioni. :) Ci vediamo!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION