CodeGym /Blog Java /Aleatoriu /Excepții: prinderea și manipularea
John Squirrels
Nivel
San Francisco

Excepții: prinderea și manipularea

Publicat în grup
Bună! Nu-mi place să menționez asta, dar o mare parte din munca unui programator se ocupă de erori. Cel mai adesea, al lui sau ei. Se dovedește că nu există oameni care să nu greșească. Și nici astfel de programe nu există. Excepții: prinderea și manipularea - 1 Desigur, atunci când aveți de-a face cu o eroare, principalul lucru este să înțelegeți cauza acesteia. Și o mulțime de lucruri pot cauza erori într-un program. La un moment dat, creatorii Java s-au întrebat ce ar trebui făcut cu cele mai probabile erori de programare? Evitarea lor totală nu este realistă, programatorii sunt capabili să scrie lucruri pe care nici nu ți le poți imagina. :) Deci, trebuie să dăm limbajului un mecanism de lucru cu erori. Cu alte cuvinte, dacă există o eroare în programul dvs., aveți nevoie de un fel de script pentru ce să faceți în continuare. Ce ar trebui să facă exact un program când apare o eroare? Astăzi ne vom familiariza cu acest mecanism. Se numește „ excepții în Java ”.

Ce este o excepție?

O excepție este o situație excepțională, neplanificată, care apare în timpul rulării unui program. Există multe excepții. De exemplu, ați scris cod care citește text dintr-un fișier și afișează prima linie.

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);
   }
}
Dar dacă nu există un astfel de fișier! Programul va genera o excepție: FileNotFoundException. Ieșire: Excepție în firul „principal” java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Sistemul nu poate găsi calea specificată) În Java, fiecare excepție este reprezentată de o clasă separată. Toate aceste clase de excepție derivă dintr-un „strămoș” comun – Throwableclasa părinte. Numele unei clase de excepție reflectă, de obicei, în mod concis de ce a apărut excepția:
  • FileNotFoundException(fisierul nu a fost gasit)

  • ArithmeticException(a apărut o excepție în timpul efectuării unei operații matematice)

  • ArrayIndexOutOfBoundsException(indicele este dincolo de limitele matricei). De exemplu, această excepție apare dacă încercați să afișați poziția 23 a unui tablou care are doar 10 elemente.
În total, Java are aproape 400 de astfel de clase! De ce atât de multe? Pentru a le face mai convenabile pentru programatori. Imaginează-ți asta: scrii un program și, în timp ce rulează, generează o excepție care arată astfel:

Exception in thread "main"
Uhhhh. :/ Asta nu ajută prea mult. Nu este clar ce înseamnă eroarea sau de unde a venit. Nu există informații utile aici. Dar varietatea mare de clase de excepție din Java oferă programatorului ceea ce contează cel mai mult: tipul de eroare și cauza ei probabilă (încorporată în numele clasei). Este cu totul altceva de văzut

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Este imediat clar care ar putea fi problema și de unde să începeți să sapă pentru a rezolva problema! Excepțiile, ca și instanțe ale oricăror clase, sunt obiecte.

Prinderea și gestionarea excepțiilor

Java are blocuri speciale de cod pentru lucrul cu excepții: try, catchși finally. Excepții: prinderea și manipularea - 2 Codul în care programatorul crede că poate apărea o excepție este plasat în trybloc. Asta nu înseamnă că aici va apărea o excepție. Înseamnă că ar putea să apară aici, iar programatorul este conștient de această posibilitate. Tipul de eroare pe care vă așteptați să apară este plasat în catchbloc. Acesta conține și tot codul care ar trebui executat dacă apare o excepție. Iată un exemplu:

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!");
   }
}
Ieșire: Eroare! Fișierul nu a fost găsit! Ne punem codul în două blocuri. În primul bloc, anticipăm că poate apărea o eroare „Fișier nu a fost găsit”. Acesta este tryblocul. În al doilea, îi spunem programului ce trebuie să facă dacă apare o eroare. Și tipul de eroare specific: FileNotFoundException. Dacă punem o clasă de excepție diferită în parantezele blocului catch, atunci FileNotFoundExceptionnu va fi prins.

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!");
   }
}
Ieșire: Excepție în firul „principal” java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Sistemul nu poate găsi calea specificată) Codul din catchbloc nu a rulat, deoarece am „configurat” acest bloc pentru a prinde ArithmeticException, iar codul din trybloc a aruncat un alt tip: FileNotFoundException. Nu am scris niciun cod de tratat FileNotFoundException, așa că programul afișează informațiile implicite pentru FileNotFoundException. Aici trebuie să fiți atenți la trei lucruri. Numărul unu. Odată ce apare o excepție pe o linie din trybloc, codul care urmează nu va fi executat. Execuția programului „sare” imediat la catchbloc. De exemplu:

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!");
   }
}
Ieșire: Împărțire la zero Programul a sărit la blocul catch! Eroare! Nu poți împărți la zero! Pe a doua linie a tryblocului, încercăm să împărțim la 0, rezultând un ArithmeticException. În consecință, liniile 3-9 ale tryblocului nu vor fi executate. După cum am spus, programul începe imediat să execute catchblocul. Numarul doi. Pot exista mai multe catchblocuri. Dacă codul din trybloc ar putea arunca nu una, ci mai multe tipuri diferite de excepții, puteți scrie un catchbloc pentru fiecare dintre ele.

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!");
      
   }
}
În acest exemplu, am scris două catchblocuri. Dacă FileNotFoundExceptionapare în trybloc, atunci primul catchbloc va fi executat. Dacă ArithmeticExceptionapare, al doilea bloc va fi executat. Ai putea scrie 50 catchde blocuri dacă ai vrea. Desigur, este mai bine să nu scrieți cod care ar putea arunca 50 de tipuri diferite de excepții. :) În al treilea rând. De unde știi ce excepții ar putea arunca codul tău? Ei bine, poate le poți ghici pe unele dintre ele, dar îți este imposibil să ții totul în cap. Prin urmare, compilatorul Java cunoaște cele mai comune excepții și situațiile în care acestea pot apărea. De exemplu, dacă scrieți cod despre care compilatorul știe că ar putea arunca două tipuri de excepții, codul dvs. nu se va compila până când nu le gestionați. Vom vedea exemple în acest sens mai jos. Acum câteva cuvinte despre gestionarea excepțiilor. Există 2 moduri de a gestiona excepțiile. L-am întâlnit deja pe primul: metoda poate gestiona excepția în sine într-un catch()bloc. Există o a doua opțiune: metoda poate arunca din nou excepția în stiva de apeluri. Ce înseamnă asta? De exemplu, avem o clasă cu aceeași printFirstString()metodă, care citește un fișier și afișează prima linie:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
În prezent, codul nostru nu se compila, deoarece are excepții negestionate. În linia 1, specificați calea către fișier. Compilatorul știe că un astfel de cod ar putea produce cu ușurință un FileNotFoundException. În rândul 3, citiți textul din fișier. Acest proces ar putea duce cu ușurință la o IOException(eroare de intrare/ieșire). Acum compilatorul îți spune: „Omule, nu voi aproba acest cod și nu-l voi compila până nu-mi spui ce ar trebui să fac dacă apare una dintre aceste excepții. Și cu siguranță s-ar putea întâmpla pe baza codului pe care l-ai scris. !" Nu există nicio modalitate de a ocoli: trebuie să te descurci cu ambele! Știm deja despre prima metodă de gestionare a excepțiilor: trebuie să punem codul nostru într-un trybloc și să adăugăm două catchblocuri:

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();
   }
}
Dar aceasta nu este singura opțiune. Am putea pur și simplu să aruncăm excepția mai sus, în loc să scriem cod de gestionare a erorilor în cadrul metodei. Acest lucru se face folosind cuvântul cheie throwsdin declarația metodei:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
După cuvântul cheie throws, indicăm o listă separată prin virgulă cu toate tipurile de excepții pe care metoda le-ar putea arunca. De ce? Acum, dacă cineva dorește să apeleze printFirstString()metoda din program, el sau ea (nu tu) va trebui să implementeze gestionarea excepțiilor. De exemplu, să presupunem că în altă parte a programului unul dintre colegii tăi a scris o metodă care vă numește printFirstString()metoda:

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");
}
Primim o eroare! Acest cod nu se va compila! Nu am scris cod de tratare a excepțiilor în printFirstString()metodă. Drept urmare, această sarcină cade acum pe umerii celor care folosesc metoda. Cu alte cuvinte, methodWrittenByYourColleague()metoda are acum aceleași 2 opțiuni: fie trebuie să folosească un try-catchbloc pentru a gestiona ambele excepții, fie să le arunce din nou.

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");
}
În al doilea caz, următoarea metodă din stiva de apeluri — cea care apelează methodWrittenByYourColleague()— va trebui să gestioneze excepțiile. De aceea numim acest lucru „a aruncare sau a trece excepția în sus”. Dacă aruncați excepții în sus folosind cuvântul cheie throws, codul dvs. se va compila. În acest moment, compilatorul pare să spună: „Bine, bine. Codul tău conține o grămadă de potențiale excepții, dar îl voi compila. Dar vom reveni la această conversație!” Și când apelați orice metodă care are excepții negestionate, compilatorul își îndeplinește promisiunea și vă reamintește despre ele din nou. În cele din urmă, vom vorbi despre finallyblocaj (scuze pentru joc de cuvinte). Aceasta este ultima parte a try-catch-finallytriumviratului de gestionare a excepțiilor..

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!");
   }
}
În acest exemplu, codul din interiorul finallyblocului va fi executat în ambele cazuri. Dacă codul din trybloc este rulat în întregime fără a arunca nicio excepție, blocul finallyva rula în cele din urmă. Dacă codul din interiorul tryblocului este întrerupt de o excepție și programul sare la catchbloc, finallyblocul va rula în continuare după codul din interiorul blocului catch. De ce este necesar acest lucru? Scopul său principal este de a executa cod obligatoriu: cod care trebuie efectuat indiferent de circumstanțe. De exemplu, adesea eliberează unele resurse utilizate de program. În codul nostru, deschidem un flux pentru a citi informațiile din fișier și le transmitem obiectului BufferedReader. Trebuie să ne închidem cititorul și să eliberăm resursele. Acest lucru trebuie făcut indiferent de ce - când programul funcționează așa cum ar trebui și când lansează o excepție. Blocul finallyeste un loc foarte convenabil pentru a face acest lucru:

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();
       }
   }
}
Acum suntem siguri că ne vom ocupa de resurse, indiferent de ce se întâmplă când programul rulează. :) Asta nu este tot ce trebuie să știi despre excepții. Gestionarea erorilor este un subiect foarte important în programare. O mulțime de articole îi sunt dedicate. În lecția următoare, vom afla ce tipuri de excepții există și cum să vă creați propriile excepții. :) Ne vedem atunci!
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION