JavaRush /Java Blog /Random-IT /Eccezioni in Java: cattura e gestione

Eccezioni in Java: cattura e gestione

Pubblicato nel gruppo Random-IT
Ciao! Odio dirtelo, ma gran parte del lavoro di un programmatore riguarda gli errori. E molto spesso - con i propri. Si dà il caso che non ci siano persone che non commettano errori. E non esistono nemmeno programmi del genere. Naturalmente, la cosa principale quando si lavora su un errore è capirne la causa. E ci possono essere tutta una serie di ragioni per questo nel programma. Ad un certo punto, i creatori di Java si sono trovati di fronte alla domanda: cosa fare con questi potenziali errori nei programmi? Evitarli completamente non è realistico. I programmatori possono scrivere qualcosa che è impossibile anche solo immaginare :) Ciò significa che è necessario incorporare nel linguaggio un meccanismo per gestire gli errori. In altre parole, se si è verificato un errore nel programma, è necessario uno script per proseguire il lavoro. Cosa dovrebbe fare esattamente il programma quando si verifica un errore? Oggi faremo conoscenza con questo meccanismo. E si chiama “Eccezioni .

Cos'è un'eccezione in Java

Un'eccezione è una situazione eccezionale e non pianificata che si è verificata durante il funzionamento del programma. Ci possono essere molti esempi di eccezioni in Java. Ad esempio, hai scritto un codice che legge il testo da un file e visualizza la prima riga nella console.
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 un file del genere non esiste! Il risultato del programma sarà un'eccezione - FileNotFoundException. Conclusione:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Ogni eccezione è rappresentata da una classe separata in Java. Tutte le classi di eccezioni provengono da un "antenato" comune: la classe genitore Throwable. Il nome della classe di eccezione di solito riflette brevemente il motivo della sua occorrenza:
  • FileNotFoundException(file non trovato)
  • ArithmeticException(eccezione quando si esegue un'operazione matematica)
  • ArrayIndexOutOfBoundsException(il numero della cella dell'array è specificato oltre la sua lunghezza). Ad esempio, se provi a inviare l'array di celle[23] alla console per un array di lunghezza 10.
Ci sono quasi 400 classi di questo tipo in Java! Perchè così tanti? Proprio per rendere più conveniente per i programmatori lavorare con loro. Immagina: hai scritto un programma e, quando viene eseguito, genera un'eccezione simile a questa:
Exception in thread "main"
Uh-uh:/ Niente è chiaro. Che tipo di errore sia e da dove provenga non è chiaro. Non ci sono informazioni utili. Ma grazie a una tale varietà di classi, il programmatore ottiene per sé la cosa principale: il tipo di errore e la sua probabile causa, che è contenuta nel nome della classe. Dopotutto, è una cosa completamente diversa da vedere nella console:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Diventa subito chiaro quale potrebbe essere il problema e “in che direzione scavare” per risolvere il problema! Le eccezioni, come qualsiasi istanza di classi, sono oggetti.

Catturare e gestire le eccezioni

Per lavorare con le eccezioni in Java, esistono blocchi di codice speciali: try, catche finally. Eccezioni: intercettazione e trattamento - 2Il codice in cui il programmatore si aspetta che si verifichino delle eccezioni viene inserito in un blocco try. Ciò non significa che in questa posizione si verificherà necessariamente un'eccezione. Ciò significa che può accadere lì e il programmatore ne è consapevole. Il tipo di errore che ti aspetti di ricevere viene inserito in un blocco catch("catch"). Qui è anche dove viene inserito tutto il codice che deve 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!");
   }
}
Conclusione:

Ошибка! Файл не найден!
Mettiamo il nostro codice in due blocchi. Nel primo blocco ci aspettiamo che possa verificarsi un errore "File non trovato". Questo è un blocco try. Nella seconda diciamo al programma cosa fare se si verifica un errore. Inoltre, esiste un tipo specifico di errore: FileNotFoundException. Se passiamo catchun'altra classe di eccezione nelle parentesi del blocco, non verrà catturata.
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!");
   }
}
Conclusione:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Il codice nel blocco catchnon ha funzionato perché abbiamo "configurato" questo blocco per intercettare ArithmeticExceptione il codice nel blocco tryha eliminato un altro tipo - FileNotFoundException. Non abbiamo scritto uno script per FileNotFoundException, quindi il programma visualizza nella console le informazioni visualizzate per impostazione predefinita per FileNotFoundException. Qui devi prestare attenzione a 3 cose. Primo. Non appena si verifica un'eccezione in qualsiasi riga di codice in un blocco try, il codice successivo non verrà più eseguito. L'esecuzione del programma “salta” immediatamente al blocco catch. Per esempio:
public static void main(String[] args) {
   try {
       System.out.println("Divide a number 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("Not");
       System.out.println("will");
       System.out.println("done!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Conclusione:

Делим число на ноль 
Программа перепрыгнула в блок catch! 
Ошибка! Нельзя делить на ноль! 
Nel blocco trydella seconda riga abbiamo provato a dividere un numero per 0, ottenendo un'eccezione ArithmeticException. Successivamente, le righe 6-10 del blocco trynon verranno più eseguite. Come abbiamo detto il programma ha subito iniziato l'esecuzione del blocco catch. Secondo. Possono esserci diversi blocchi catch. Se il codice in un blocco trypuò generare non uno, ma diversi tipi di eccezioni, puoi scrivere il tuo blocco per ciascuno di essi catch.
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 blocchi catch. Se , trysi verifica nel blocco FileNotFoundException, verrà eseguito il primo blocco catch. Se succede ArithmeticException, verrà eseguito il secondo. Puoi scrivere almeno 50 blocchi catch, ma ovviamente è meglio non scrivere codice che possa generare 50 diversi tipi di errori :) Terzo. Come fai a sapere quali eccezioni potrebbe generare il tuo codice? Beh, ovviamente puoi indovinarne alcuni, ma è impossibile tenere tutto nella tua testa. Pertanto, il compilatore Java conosce le eccezioni più comuni e sa in quali situazioni possono verificarsi. Ad esempio, se hai scritto codice e il compilatore sa che possono verificarsi 2 tipi di eccezioni durante il suo funzionamento, il tuo codice non verrà compilato finché non li gestisci. Vedremo esempi di ciò di seguito. Ora per quanto riguarda la gestione delle eccezioni. Ci sono 2 modi per elaborarli. Abbiamo già incontrato il primo: il metodo può gestire l'eccezione in modo indipendente nel blocco catch(). Esiste una seconda opzione: il metodo può generare un'eccezione nello stack di chiamate. Cosa significa? Ad esempio, nella nostra classe abbiamo un metodo - lo stesso printFirstString()- che legge un file e ne visualizza la prima riga sulla console:
public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Attualmente il nostro codice non viene compilato perché presenta eccezioni non gestite. Nella riga 1 si indica il percorso del file. Il compilatore sa che tale codice può facilmente portare a FileNotFoundException. Alla riga 3 leggi il testo dal file. In questo processo può facilmente verificarsi IOExceptionun errore durante l'input-output (Input-Output) . Ora il compilatore ti sta dicendo: "Amico, non approverò questo codice né lo compilerò finché non mi dirai cosa dovrei fare se si verifica una di queste eccezioni. E possono sicuramente verificarsi in base al codice che hai scritto!” . Non c'è nessun posto dove andare, devi elaborarli entrambi! La prima opzione di elaborazione ci è già familiare: dobbiamo inserire il nostro codice in un blocco trye aggiungere due blocchi catch:
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("Error while inputting/outputting data from file!");
       e.printStackTrace();
   }
}
Ma questa non è l’unica opzione. Possiamo evitare di scrivere uno script per l'errore all'interno del metodo e semplicemente lanciare l'eccezione in alto. Questo viene fatto utilizzando la parola chiave throws, che è scritta nella 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 throwselenchiamo, separati da virgole, tutti i tipi di eccezioni che questo metodo può lanciare durante il funzionamento. Perché viene fatto questo? Ora, se qualcuno nel programma vuole chiamare il metodo printFirstString(), dovrà implementare lui stesso la gestione delle eccezioni. Ad esempio, in un'altra parte del programma, uno dei tuoi colleghi ha scritto un metodo all'interno del quale chiama il tuo metodo printFirstString():
public static void yourColleagueMethod() {

   //...your colleague's method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Errore, il codice non viene compilato! printFirstString()Non abbiamo scritto uno script di gestione degli errori nel metodo . Pertanto il compito ricade sulle spalle di chi utilizzerà questo metodo. Cioè, il metodo yourColleagueMethod()ora deve affrontare le stesse 2 opzioni: deve elaborare entrambe le eccezioni che “arrivano” utilizzando try-catchoppure inoltrarle ulteriormente.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   //...the method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Nel secondo caso, l'elaborazione ricadrà sulle spalle del metodo successivo nello stack, quello che chiamerà yourColleagueMethod(). Questo è il motivo per cui un tale meccanismo si chiama "lanciare un'eccezione verso l'alto" o "passare verso l'alto". Quando si generano eccezioni utilizzando throws, il codice viene compilato. In questo momento, il compilatore sembra dire: “Va bene, va bene. Il tuo codice contiene un sacco di potenziali eccezioni, ma lo compilerò comunque. Torneremo su questa conversazione! E quando chiami un metodo da qualche parte nel programma che non ha gestito le sue eccezioni, il compilatore mantiene la sua promessa e te lo ricorda di nuovo. Infine parleremo del blocco finally(scusate il gioco di parole). Questa è l'ultima parte dell'eccezione che gestisce triumvirate try-catch-finally. La sua particolarità è che viene eseguito in qualsiasi scenario operativo del programma.
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 is the finally block!");
   }
}
In questo esempio il codice all'interno del blocco finallyviene eseguito in entrambi i casi. Se il codice nel blocco tryviene eseguito interamente e non genera un'eccezione, il blocco verrà attivato alla fine finally. Se il codice interno tryviene interrotto e il programma salta al blocco catch, dopo l'esecuzione del codice interno catch, il blocco sarà ancora selezionato finally. Perché è necessario? Il suo scopo principale è eseguire la parte richiesta del codice; quella parte che deve essere completata indipendentemente dalle circostanze. Ad esempio, spesso libera alcune risorse utilizzate dal programma. Nel nostro codice, apriamo uno stream per leggere le informazioni da un file e passarle a un file BufferedReader. Il nostro readerdeve essere chiuso e liberate risorse. Questo deve essere fatto in ogni caso: non importa se il programma funziona come previsto o genera un'eccezione. È conveniente farlo in un blocco finally:
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 is the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Ora siamo assolutamente sicuri di esserci presi cura delle risorse occupate, indipendentemente da ciò che accade mentre il programma è in esecuzione :) Non è tutto ciò che devi sapere sulle eccezioni. La gestione degli errori è un argomento molto importante nella programmazione: ad esso è dedicato più di un articolo. Nella prossima lezione impareremo quali tipi di eccezioni esistono e come creare la tua eccezione :) Ci vediamo lì!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION