încercați / captura / în cele din urmă și excepții
Cred că este greu de un Java-dezvoltator, care nu este cel puțin auzit de excepții. În principiu, ele nu sunt proprietatea exclusivă a Java, acolo sunt în Delphi, a pus în C ++. Cu toate acestea, în excepțiile Java sunt utilizate în special pe scară largă. Și de multe ori acestea sunt asociate cu o mulțime de greșeli. Scopul principal al acestui articol - sistematizarea informațiilor privind excepțiile, prelucrarea acestora, etc. Și anume:
Excluderea ca fenomen
Care este excepția generală? Acest semnal este un non - exclusiv - situație. Situația poate fi foarte diferite - de așteptat, sau nu, de diferite grade de severitate. Și trata aceste situații, desigur, avem în multe feluri.
Ca totul în Java, de asemenea, excepțiile sunt reprezentate ca clase. Rădăcina ierarhiei este clasa java.lang.Throwable. literalmente - „arunca“. descendenții săi direcți sunt java.lang.Exception și java.lang.Error. de la care a moștenit toate celelalte excepții. Și de la care este recomandat să moștenească proprietatea.
Vreau să subliniez faptul că excluderea nu este ceva ieșit din comun. Acesta este mecanismul normal. Deci, nu încercați să evitați utilizarea lor la toate costurile. Și cu atât mai mult nu ar trebui să încerce să evite excepții în constructori, care au tendința de a dezvoltatorilor familiarizați cu C ++. Având în vedere pierderi de memorie gunoier, în acest caz, nu va. Este într-adevăr că încercați. Adică în mod intenționat.
Acum, ia în considerare tipurile de excepții mai aproape.
excepție de clasificare
Așa cum am menționat, există două tipuri de „bază“ de excepții - java.lang.Exception și java.lang.Error. Aș spune că diferitele grade de severitate. Nu este, desigur, ambele tipuri pot fi interceptate, pentru excepțiile de captură de la nivelul java.lang.Throwable. Această diviziune este destul de logic. Dacă era ceva serios - nu a găsit o metodă pe care trebuie să suni, peste stiva de memorie preaplin, pe scurt, ceva, apoi restabili funcționarea normală, este puțin probabil să într-adevăr - aceasta este o greșeală, java.lang.Error. Dacă munca suplimentară este teoretic posibil - aceasta este o excepție, java.lang.Exception.
Pe lângă deja menționate două tipuri, există, de asemenea, o altă clasă, care este esențială - java.lang.RuntimeException. El a moștenit de la java.lang.Exception, și este rădăcina pentru toate excepțiile de execuție - și anume întâlnite atunci când se utilizează o mașină virtuală. Referința la zero, divizia de la zero, un argument invalid, merge dincolo de matrice, etc. - toate excepțiile de rulare. Cum diferă ele de obicei, ne vom uita la mai jos.
Ca o excepție - exact aceleași clase, ele pot fi pornite și propriile variabile, și chiar un fel de logică. Implică-te, cu toate acestea, nu este necesar. Faptul că excluderea - clasa este încă evidențiată. Când creați un Dispensabil există o mare deasupra capului - umplerea stiva de apel. Aceasta este o procedură destul de consumatoare de timp. Și astfel să creeze o excepție pentru un motiv - propriul nostru pericol. Ar trebui să fie creată numai atunci când este indispensabil, atunci când acesta va fi aruncat cu acuratețe. Mai ușor să facă acest lucru imediat, împreună cu o aruncare de directivă.
Dar, să păstreze în clasa excepție unele informații pe care le va permite să proces - este foarte posibil. Transmiteți aceste informații printr-un designer mai bun.
Deci, vom proceda la o analiză mai detaliată. Primul tip de bază -
java.lang.Exception
Excepții de acest tip I ar oharakteriroval așa - ele apar în situații care sunt dincolo de controlul nostru. De exemplu, am deserializuem de clasă, iar datele în formatul greșit. Și noi acest lucru se poate face nimic, dar pentru a răspunde în mod adecvat.
Orice excepție de tip java.lang.Exception care urmează să fie prelucrate. Este legea. executarea acestuia următoarele compilatoare. Dacă excepția nu este tratată în metoda - aceasta trebuie declarată în semnătura sa în acest caz tratat de mai sus. Sau chiar mai mare. Dar va fi procesată. Singura excepție - este java.lang.RuntimeException. pe care o vom discuta în continuare.
Metoda poate declara orice excepții kolichestko. Nu, oricine - este, probabil, un cuvânt prea puternic. În cazul în care memoria mea mă servește, numărul de scutiri este limitat la 64K - 65536 bucăți. Pentru mine, înseamnă deja că eu pot declara cât mai multe excepții ca vreau.
java.lang.RuntimeException
Cum era de așteptat eroare - prost, excepții de tip java.lang.RuntimeException nu sunt declarate. Compilatorul permite, respectiv, permițându-le să nu prelucreze. Mai mult decât atât, aș spune că java.lang.RuntimeException de captură - sub formă de rău, pentru că în loc de a avea pentru a elimina cauza, am neystralizuem consecințe. Și cel mai rău este faptul că dezvoltatorul elimina erorile în timpul aproape imposibil, de fapt, ele sunt mult mai aproape de java.lang.Error criticitate.
Să ne întoarcem acum la ultimul tip -
java.lang.Error
Așa cum am menționat, este erori critice, după care a restabili funcționarea normală practic imposibilă. De fapt - ei bine, ce se poate face atunci când o stivă de preaplin? Sau, în cazul în care metoda nu este găsit? Sau, dacă metoda abstractă se numește? Sau, în cazul unei erori în clasa bytecode? Absolut nimic. Cel puțin, nici o intervenție umană serioasă, care ar putea înlocui biblioteca, de exemplu. Sau schimba classpath.
În unele cazuri, situația nu este atât de critică. De exemplu, lipsa de memorie, cauzând java.lang.OutOfMemoryError. Dacă apare această eroare la momentul eliberării de cantitate mare de memorie - de exemplu, atunci când creați o matrice - este posibil să intercepteze și să încerce să aloce memorie în cantități mai mici, prin schimbarea intr-un fel algoritm care va utiliza această memorie.
Sau, să zicem, în cazul în care JVM nu suportă sistemul de codificare - o eroare va fi aruncat atunci când încercați să creați un InputStreamReader fără codare. Cu toate acestea, nu avem nevoie de un sistem de codificare și de modul în care critică absența ei pentru noi - este de până la noi.
Ca și în cazul compilatorul rezolvă java.lang.RuntimeException nu declară și trata erorile din ierarhia java.lang.Error. Prinde-le sau nu - a vă decide. Depinde dacă vă poate oferi un algoritm acceptabil pentru a recupera.
Odată cu clasificarea terminat, vom trece la următoarea întrebare.
inițierea de excepții
Și la următoarea întrebare - ce și când să arunce.
Imaginați-vă că creați situații excepționale și pentru a lucra la acest cod nu se poate. Ce tip de excepție de utilizat?
Este timpul pentru a profita de caracteristicile excepțiile enumerate mai sus. În cazul în care o situație excepțională a apărut este că munca în continuare este imposibilă, în principiu - poate arunca o excepție de la ierarhia java.lang.Error. Deși eu personal nu am avut astfel de situații în practică. În cazul în care o situație excepțională a apărut din vina unor circumstanțe externe sau utilizatorul aplicației - nu datele furnizate, de exemplu - atunci este un caz în care erorile trebuie să fie prelucrate, prin urmare, cel mai bine este de a utiliza un java.lang.Exception. În cazul în care situația a apărut din vina dezvoltator - de exemplu, am trimis ca parametru nul. în ciuda faptului că, în limba engleză vă javadoc și alb, că acest lucru nu se poate face - atunci java.lang.RuntimeException.
Câteva cuvinte despre exemplul de mai sus - nule ca parametru. Teoretic, în această situație, vă poate arunca două excepții - NullPointerException și IllegalArgumentException. Neskotrya alegerea evidentă aparent, eu personal prefer a doua opțiune. În opinia mea, ar trebui să arunce NullPointerException mașină virtuală în mod exclusiv. Dacă am făcut eu verifica valoarea și asigurați-vă că acesta este nul. dar acest lucru nu ar trebui să fie - mult mai informativ pentru a utiliza IllegalArgumentException. în cazul în care valoarea argumentului sau IllegalStateException - în cazul în care această valoare este un membru al clasei.
Ei bine, că arunca - ne definim cumva. Dar, uneori, există o anumită dorință irațională, nu numai pentru a arunca o excepție, dar într-un fel trupul său, precizând motivul. Nu, nu poți, desigur, și excluderea fiecărei erori set, dar du-te nebun mult mai ușor și mai rapid.
Prima variantă, care este văzut - un mesaj text. Aproape orice clasă de excepție are un constructor care ia ca parametru text. Este acest text apare în consola când se imprimă mesajul de excepție, este posibil pentru a obține prin intermediul getMessage (). Acesta pozlezno atunci când este detectată o eroare de către noi înșine. Și ce să facă atunci când o greșeală pentru noi „străine“, spune - am prins excepție aruncat mai adânc?
În acest caz, mecanismul poate fi așa-numita cauțiune excepție înlănțuire - excepții obligatorii. Aproape fiecare clasă de excepție are un constructor care ia ca parametru Dispensabil - motivul excepției. Dacă acest constructor nu este - deloc același Dispensabil. din care a moștenit toate excepțiile au metoda initCause (Dispensabil). care poate fi numit exact o dată. Și dă-i motivul pentru care yavivsheesya excepție a fost inițiată în urma excepție.
De ce o fac. Faptul că un cod bine conceput - este ca o cutie neagră. Ea are propria interfață, un anumit comportament, un set de excepții, în cele din urmă. Și ce se întâmplă în interior - nu contează pentru ceea ce este în afara. Mai mult decât atât - uneori poate juca efectul opus. Motivele pentru care eroarea poate fi o duzină, și le prinde separat și în același proces. Cel mai adesea, pur și simplu nu au nevoie. Este mai ușor să se determine, deoarece, de exemplu, excluderea acestuia (sau deja existente, nu contează) și aruncă-l este listat ca cauza a ceea ce am prins. Și lupii sunt hrănite și utilizatorii codului de lucru mai puțin.
Ei bine, de la inițierea excepțiilor ne deplasăm, treptat, la tratamentul lor.
Manipularea excepțiilor
S-ar părea că ceea ce ar putea fi mai simplu? Am scris de captură (th Dispensabil) - și totul. Cu toate acestea, este de la asta, vreau să-i avertizeze.
În primul rând, după cum am menționat mai sus. capturare excepții de rulare - formă proastă. Ele indică eroarea. Prinde java.lang.Error sau derivați în valoare doar dacă știi exact ceea ce faci. Recuperarea în urma unor astfel de erori nu este întotdeauna posibil și aproape întotdeauna banal.
În al doilea rând - cum să se ocupe? Am scris despre asta într-un articol despre calitatea secțiunii Codul de tratare a excepțiilor. dar repet. Pentru fiecare Poymanov eliminând necesitatea de a lua o decizie. Doar înghiți, sau pentru a aduce la consola - lucrul cel mai neplăcut, care poate veni. Și acest păcat, din păcate, mulți. Un om citește XML dintr-un fișier, primește excepția înghite - și încercând să continue să lucreze cu XML ca și cum nimic nu sa întâmplat. Firește devine NullPointerException. ca clasa pe care-l folosește, nu este inițializat. Eroare dezvoltator, deși nu atât de evident.
De ce spun că ar trebui să înceapă propriile lor tipuri de excepții - astfel încât să fie mai ușor să fie în măsură să le selectați în etapa de procesare. Imaginați-vă că există cinci motive pentru care o excepție poate fi aruncat afară, și în toate cele cinci cazuri aruncă java.lang.Exception. Tu dormi imaginind ce a cauzat exact această excepție. Și dacă este de cinci tipuri diferite - nu este ușor. Pentru fiecare - o captură unitate.
Iar al treilea - ce să facă cu ierarhii de excepții. Să presupunem că aveți o metodă poate arunca ca IOException. și o excepție. Deci, nu este la fel, în ce ordine vor sta blocuri de captură. deoarece acestea sunt manipulate în exact aceeași secvență ca anunțată. Și în cazul în care prima este de captură (Excepție ex) - al doilea de management (captura IOException ioex) () pur și simplu nu înțeleg. Compilatorul de acest lucru, desigur, avertizează, de altfel - este considerată o greșeală. Cu toate acestea, merită să ne amintim.
Următorul lucru pe care aș dori să atingă -
excepțiile Intercept cauzate de finalizarea fluxului de
Atunci când se utilizează mai multe fire, există situații când este necesar să se știe cum să curgă peste. Vreau să spun - în cazul în care acest lucru se datorează o excepție, din cauza asta. În acest scop, începând cu eliberarea de Java 5, există o interfață specială - Thread.UncaughtExceptionHandler. Implementarea acestuia se poate seta debitul dorit prin metoda setUncaughtExceptionHandler. Puteți seta, de asemenea, handler implicit cu metoda Thread.setDefaultUncaughtExceptionHandler statică.
Interfața Thread.UncaughtExceptionHandler are o singură metodă - uncaughtException (filet t, Dispensabil e) - care este transmis în instanța de flux se încheie o excepție, și o copie a excepției. Restabilește fluxul natural, nu va reuși, ci să se stabilească faptul încetării sale anormale, astfel, este posibil.
Următorul lucru pe care am vrut să vorbesc - ea încerca / captură / în cele din urmă construi.
Design-ul try / catch / în cele din urmă - subtilitate
Din blocul try. Cred că întrebarea nu se pune. Cu blocul de captură - speranță, deja nu sunt. Acesta rămâne în cele din urmă bloc. Ce este și ce mănâncă?
Blocul este în cele din urmă în cele din urmă constructului try / catch / în cele din urmă. O caracteristică esențială a acesteia este că el este întotdeauna mulțumit. indiferent dacă sunt sau nu captura de sarcină.
De ce o fac. Uita-te aici, pe această bucată de cod:
S-ar părea că totul este în ordine. Și ce se va întâmpla dacă, de exemplu, atunci când apelați rs.first () se întâmplă o excepție? Da, aceasta este procesată. Cu toate acestea, nici ResultSet. nici o declarație. sau o conexiune nu vor fi închise. Consecințele sunt foarte trist - epuizarea cursoare deschise, conexiuni în piscina etc.
Puteți, desigur, pentru a pune același cod în blocul de captură. Cu toate acestea, duplicat codul - nu cea mai bună idee. Corectă pentru a face un cod de închidere în blocul în cele din urmă. În acest caz, acesta va fi executat ca și în cazul unei excepții, și când nu.
Luați în considerare un alt exemplu:
Tricky întrebare - Care este rezultatul? Noi efectua teste și pentru a obține:
Ie În ambele cazuri, rezultatul va fi același. și nu una care v-ați aștepta! De ce se întâmplă acest lucru? În primul caz - a blocului try ar trebui să returneze o valoare de 6 - lungimea șirului trecut. Cu toate acestea, în cele din urmă să blocheze. în care funcția returnează 0. Ca urmare, valoarea inițială se pierde. În al doilea caz - o încercare de a provoca toString () atunci când valoarea nulă transmisă ar elimina - NullPointerException. Această excepție va fi prins, deoarece Acesta este succesorul de excepție. Din blocul de captură ar trebui să returneze o valoare de -1, dar în cele din urmă se întoarce din nou blocului și 0, și -1 - a pierdut!
În mod similar, blocul în cele din urmă poate duce la pierderea de excepții. Uită-te aici, la acest exemplu:
Acest test este da foarte des un interviu. Și unitate a răspuns în mod corect. Între timp - rezultatul executării sale va fi de ieșire în consolă b. Și numai. După inițierea primei excepții - o nouă excepție ( „a“) - în cele din urmă blocul este executat. în care va fi aruncat o nouă excepție IOException ( „b“). Și este această excepție va fi prins și manipulate. aceeași excepție inițială se pierde.
Aș dori să atingă pe un alt punct. În ce combinații pot exista blocuri încerca. captură, și în cele din urmă. Se înțelege că încercați - este obligatorie. Acesta poate fi partajat cu captura. sau o captură, și în cele din urmă, în același timp. Separat, nu încercați folosit. Există și alte opțiuni?
Acolo. încercați poate fi asociat cu cele din urmă. Nici un profit. Acesta funcționează în același mod - după ieșirea din blocul try, în cele din urmă să blocheze. Acest lucru poate fi util, de exemplu, în următoarea situație. Când ieșiți metoda, aveți nevoie pentru a face o acțiune. O întoarcere la această metodă costă în mai multe locuri. Pentru a scrie același cod înainte de fiecare reveni impracticabilă. Este mult mai ușor și mai eficient pentru a pune codul de bază în încercare. și cod care ruleaza la ieșire - în cele din urmă.
Și ultimul subiect -
Lipsa tranzacționale
La început am menționat că pentru a organiza o scurgere de memorie atunci când se utilizează excepții de la constructori dificile. De fapt - este posibil. Ne uităm la un exemplu.
Acest exemplu este artificială, dar numai într-o anumită măsură. El vorbește despre o caracteristică foarte importantă a excepțiilor:
Proprietățile excepții tranzacționale nu posedă - activități care au loc în blocul try la o excepție, nu a anulat pocle apariția acesteia.
Și trebuie să fie luate în considerare. Cu referire la exemplul de mai jos - este în valoare de toate operațiunile care pot duce la excludere, să efectueze cât mai curând posibil. De exemplu, pentru a verifica imediat parametrii trecut. În unele cazuri, pentru a anula deja acțiuni realizate pot fi utile la blocul în cele din urmă. Și dacă într-adevăr nevoie de un loc pentru a păstra o referință la obiectul creat, și trebuie să se facă într-un constructor - cel mai bine este să-l părăsească în cele din urmă, atunci când toate codul critic a fost deja finalizat.
Ei bine, asta-i tot. Cred că Bo Cele mai multe din acest material a fost familiar. Oricum, eu sper. Scopul meu a fost pur și simplu de a organiza informațiile pentru acele excepții încă familiarizați superficial. Va multumesc tuturor!