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ă.
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 ”.
Codul în care programatorul crede că poate apărea o excepție este plasat în

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 – Throwable
clasa 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.
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
. 
try
bloc. 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 catch
bloc. 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 try
blocul. Î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 FileNotFoundException
nu 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 catch
bloc nu a rulat, deoarece am „configurat” acest bloc pentru a prinde ArithmeticException
, iar codul din try
bloc 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 try
bloc, codul care urmează nu va fi executat. Execuția programului „sare” imediat la catch
bloc. 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 try
blocului, încercăm să împărțim la 0, rezultând un ArithmeticException
. În consecință, liniile 3-9 ale try
blocului nu vor fi executate. După cum am spus, programul începe imediat să execute catch
blocul. Numarul doi. Pot exista mai multe catch
blocuri. Dacă codul din try
bloc ar putea arunca nu una, ci mai multe tipuri diferite de excepții, puteți scrie un catch
bloc 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ă catch
blocuri. Dacă FileNotFoundException
apare în try
bloc, atunci primul catch
bloc va fi executat. Dacă ArithmeticException
apare, al doilea bloc va fi executat. Ai putea scrie 50 catch
de 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 try
bloc și să adăugăm două catch
blocuri:
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 throws
din 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-catch
bloc 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 finally
blocaj (scuze pentru joc de cuvinte). Aceasta este ultima parte a try-catch-finally
triumviratului 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 finally
blocului va fi executat în ambele cazuri. Dacă codul din try
bloc este rulat în întregime fără a arunca nicio excepție, blocul finally
va rula în cele din urmă. Dacă codul din interiorul try
blocului este întrerupt de o excepție și programul sare la catch
bloc, finally
blocul 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 finally
este 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!
Mai multe lecturi: |
---|
GO TO FULL VERSION