1. Tipuri de excepții
Toate excepțiile sunt împărțite în 4 tipuri, care sunt de fapt clase care se moștenesc unele pe altele.
Throwable
clasă
Clasa de bază pentru toate excepțiile este Throwable
clasa. Clasa Throwable
conține codul care scrie stiva de apeluri curentă (urma stivă a metodei curente) într-o matrice. Vom afla ce este o urmă de stivă puțin mai târziu.
Operatorul de aruncare poate accepta doar un obiect care derivă din Throwable
clasă. Și, deși teoretic poți scrie cod ca throw new Throwable();
, nimeni nu face asta de obicei. Scopul principal al Throwable
clasei este de a avea o singură clasă părinte pentru toate excepțiile.
Error
clasă
Următoarea clasă de excepție este Error
clasa, care moștenește direct Throwable
clasa. Mașina Java creează obiecte ale Error
clasei (și descendenții acesteia) atunci când au apărut probleme grave . De exemplu, o defecțiune hardware, memorie insuficientă etc.
De obicei, ca programator, nu poți face nimic într-o situație în care o astfel de eroare (de tipul pentru care Error
ar trebui aruncat) a apărut în program: aceste erori sunt prea grave. Tot ce puteți face este să notificați utilizatorul că programul se blochează și/sau să scrieți toate informațiile cunoscute despre eroare în jurnalul programului.
Exception
clasă
Clasele Exception
și RuntimeException
sunt pentru erori obișnuite care apar în operarea multor metode. Scopul fiecărei excepții aruncate este să fie prins de un catch
bloc care știe să o gestioneze corect.
Atunci când o metodă nu își poate finaliza activitatea dintr-un motiv oarecare, ar trebui să notifice imediat metoda de apelare, lansând o excepție de tipul adecvat.
Cu alte cuvinte, dacă o variabilă este egală cu null
, metoda va arunca un NullPointerException
. Dacă argumentele incorecte au fost transmise metodei, aceasta va arunca un InvalidArgumentException
. Dacă metoda se împarte accidental la zero, va arunca un ArithmeticException
.
RuntimeException
clasă
RuntimeExceptions
sunt un subset al Exceptions
. Am putea spune chiar că RuntimeException
este o versiune ușoară a excepțiilor obișnuite ( Exception
) — sunt impuse mai puține cerințe și restricții pentru astfel de excepții
Veți învăța diferența dintre Exception
și RuntimeException
mai târziu.
2. Throws
: excepții verificate
Toate excepțiile Java se încadrează în 2 categorii: bifate și nebifate .
Toate excepțiile care moștenesc RuntimeException
sau Error
sunt considerate excepții neverificate . Toate celelalte sunt excepții bifate .
La douăzeci de ani după ce au fost introduse excepții verificate, aproape fiecare programator Java consideră asta ca pe o eroare. În cadrele moderne populare, 95% din toate excepțiile sunt neverificate. Limbajul C#, care aproape a copiat Java exact, nu a adăugat excepții bifate .
Care este principala diferență dintre excepțiile bifate și neverificate ?
Există cerințe suplimentare impuse excepțiilor verificate . În linii mari, acestea sunt acestea:
Cerința 1
Dacă o metodă aruncă o excepție bifată , trebuie să indice tipul de excepție în semnătura sa . În acest fel, fiecare metodă care o apelează este conștientă de faptul că această „excepție semnificativă” ar putea apărea în ea.
Indicați excepțiile bifate după parametrii metodei după throws
cuvântul cheie (nu utilizați throw
cuvântul cheie din greșeală). Arata cam asa:
type method (parameters) throws exception
Exemplu:
excepție verificată | excepție nebifată |
---|---|
|
|
În exemplul din dreapta, codul nostru aruncă o excepție nebifată - nu este necesară nicio acțiune suplimentară. În exemplul din stânga, metoda aruncă o excepție bifatăthrows
, astfel încât cuvântul cheie este adăugat la semnătura metodei împreună cu tipul excepției.
Dacă o metodă se așteaptă să arunce mai multe excepții bifate , toate acestea trebuie specificate după throws
cuvântul cheie, separate prin virgule. Ordinea nu este importantă. Exemplu:
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");
}
Cerința 2
Dacă apelați o metodă care a verificat excepții în semnătură, nu puteți ignora faptul că le aruncă.
Trebuie fie să prindeți toate astfel de excepții adăugând catch
blocuri pentru fiecare, fie adăugându-le la o throws
clauză pentru metoda dvs.
Este ca și cum am spune: „ Aceste excepții sunt atât de importante încât trebuie să le prindem. Și dacă nu știm cum să le gestionăm, atunci oricine ar putea apela metoda noastră trebuie să fie anunțat că astfel de excepții pot apărea în ea.
Exemplu:
Imaginați-vă că scriem o metodă pentru a crea o lume populată de oameni. Numărul inițial de persoane este trecut ca argument. Deci trebuie să adăugăm excepții dacă sunt prea puțini oameni.
Crearea Pământului | Notă |
---|---|
|
Metoda poate arunca două excepții verificate :
|
Acest apel de metodă poate fi gestionat în 3 moduri:
1. Nu prinde nicio excepție
Acest lucru se face cel mai adesea atunci când metoda nu știe cum să gestioneze corect situația.
Cod | Notă |
---|---|
|
Metoda de apelare nu prinde excepțiile și trebuie să informeze pe alții despre ele: le adaugă la propria throws clauză |
2. Prinde unele dintre excepții
Ne ocupăm de erorile pe care le putem gestiona. Dar pe cei pe care nu îi înțelegem, îi aruncăm până la metoda de chemare. Pentru a face acest lucru, trebuie să adăugăm numele lor la clauza throws:
Cod | Notă |
---|---|
|
Apelantul prinde doar o excepție bifatăLonelyWorldException — . Cealaltă excepție trebuie adăugată la semnătura acesteia, indicând-o după throws cuvântul cheie |
3. Prinde toate excepțiile
Dacă metoda nu aruncă excepții de la metoda de apelare, atunci metoda de apelare este întotdeauna sigură că totul a funcționat bine. Și nu va putea lua nicio măsură pentru a remedia situații excepționale.
Cod | Notă |
---|---|
|
Toate excepțiile sunt prinse în această metodă. Apelantul va fi încrezător că totul a mers bine. |
3. Excepții de împachetare
Excepțiile bifate păreau cool în teorie, dar s-au dovedit a fi o mare frustrare în practică.
Să presupunem că aveți o metodă super populară în proiectul dvs. Este apelat din sute de locuri din programul dvs. Și decideți să adăugați o nouă excepție bifată . Și se poate ca această excepție verificată să fie cu adevărat importantă și atât de specială încât numai main()
metoda știe ce să facă dacă este prinsă.
Aceasta înseamnă că va trebui să adăugați excepția bifată la throws
clauza fiecărei metode care vă numește metoda super populară . La fel ca și în throws
clauza tuturor metodelor care apelează acele metode. Și a metodelor care numesc acele metode.
Ca urmare, throws
clauzele a jumătate dintre metodele din proiect primesc o nouă excepție verificată . Și, desigur, proiectul tău este acoperit de teste, iar acum testele nu se compilează. Și acum trebuie să editați și clauzele throws din testele dvs.
Și apoi tot codul tău (toate modificările din sute de fișiere) va trebui să fie revizuit de alți programatori. Și în acest moment ne întrebăm de ce am făcut am făcut atâtea schimbări sângeroase în proiect? Zilele de lucru și testele întrerupte - totul de dragul de a adăuga o excepție verificată ?
Și, bineînțeles, există încă probleme legate de moștenire și de anulare a metodei. Problemele care provin din excepțiile verificate sunt mult mai mari decât beneficiul. Concluzia este că acum puțini oameni le iubesc și puțini le folosesc.
Cu toate acestea, există încă o mulțime de cod (inclusiv cod standard de bibliotecă Java) care conține aceste excepții verificate . Ce e de făcut cu ele? Nu le putem ignora și nu știm cum să le gestionăm.
Programatorii Java au propus să încapsuleze excepțiile verificateRuntimeException
în . Cu alte cuvinte, prindeți toate excepțiile verificate și apoi creați excepții nebifate (de exemplu, RuntimeException
) și aruncați-le în schimb. Făcând asta arată cam așa:
try
{
// Code where a checked exception might occur
}
catch(Exception exp)
{
throw new RuntimeException(exp);
}
Nu este o soluție foarte frumoasă, dar nu este nimic criminal aici: excepția a fost pur și simplu îndesată într-un RuntimeException
.
Dacă doriți, îl puteți recupera cu ușurință de acolo. Exemplu:
Cod | Notă |
---|---|
|
Obțineți excepția stocată în interiorul RuntimeException obiectului. Variabila cause poate null să-și determine tipul și să o convertească într-un tip de excepție verificat . |
4. Prinderea mai multor excepții
Programatorii chiar urăsc să dubleze codul. Au venit chiar și cu un principiu de dezvoltare corespunzător: Don’t Repeat Yourself (DRY) . Dar atunci când se gestionează excepții, există ocazii frecvente când un try
bloc este urmat de mai multe catch
blocuri cu același cod.
Sau ar putea fi 3 catch
blocuri cu același cod și alte 2 catch
blocuri cu alt cod identic. Aceasta este o situație standard când proiectul dvs. gestionează excepțiile în mod responsabil.
Începând cu versiunea 7, în limbajul Java a adăugat posibilitatea de a specifica mai multe tipuri de excepții într-un singur catch
bloc. Arata cam asa:
try
{
// Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
// Exception handling code
}
Puteți avea câte catch
blocuri doriți. Cu toate acestea, un singur catch
bloc nu poate specifica excepții care se moștenesc reciproc. Cu alte cuvinte, nu puteți scrie catch ( Exception
| RuntimeException
e), deoarece RuntimeException
clasa moștenește Exception
.
5. Excepții personalizate
Puteți oricând să vă creați propria clasă de excepție. Pur și simplu creați o clasă care moștenește RuntimeException
clasa. Va arata cam asa:
class ClassName extends RuntimeException
{
}
Vom discuta detaliile pe măsură ce învățați OOP, moștenirea, constructorii și suprascrierea metodei.
Cu toate acestea, chiar dacă aveți doar o clasă simplă ca aceasta (în întregime fără cod), puteți totuși să aruncați excepții pe baza ei:
Cod | Notă |
---|---|
|
Aruncă un necontrolat MyException . |
În misiunea Java Multithreading , ne vom aprofunda în lucrul cu propriile noastre excepții personalizate.
GO TO FULL VERSION