1. İstisnalar növləri
Bütün istisnalar əslində bir-birindən miras alınmış siniflər olan 4 növə bölünür.
Throwable
sinifi
Bütün istisnalar üçün ən əsas sinif Throwable
sinifidir. Throwable
sinifində funksiyaların çağırışlarını cari stack-trace kimi bir massiva yazan kod yerləşir. Stack-trace nədir, bunu bir az sonra öyrənəcəyik.
throw operatoruna yalnız Throwable
sinifindən miras alınmış sinifin obyektini ötürmək olar. Həmçinin, nəzəri olaraq throw new Throwable();
tipli kod yazmaq mümkündür — amma adətən heç kim belə etməz. Throwable
sinifinin əsas məqsədi bütün istisnalar üçün birləşmiş sinif-prototip olmaqdır.
Error
sinifi
Növbəti istisna sinifi Error
sinifidir — bu da Throwable
sinifindən birbaşa miras alır. Error
(və onun miras alan siniflərinin) tipində obyektləri Java maşını ciddi problemlər zamanı yaradır. Məsələn, yaddaş çatışmazlığı, proqramın nasazlığı və s.
Adətən siz proqramçı kimi Error tipli bir səhv baş verdikdə heç nə edə bilməzsiniz: belə bir səhv çox ciddidir. Siz edə biləcəyiniz yeganə şey istifadəçiyə proqramın qəza ilə dayandığını bildirmək və ya səhv haqqında bütün məlum məlumatları proqramın logunda yazmaqdır.
Exception
sinifi
Exception
(və RuntimeException
) tipli istisnalar — bir çox metodların işi zamanı ortaya çıxan adi səhvlərdir. Hər bir atılan istisnanın məqsədi onu bilən catch
bloku tərəfindən tutulmaqdır.
Hər hansı bir metod hansısa səbəbdən işini yerinə yetirə bilməyəcəksə, dərhal uyğun tipdə bir istisna ataraq çağıran metoda bu barədə məlumat verməlidir.
Başqa sözlə, əgər hansısa dəyişən null
olarsa, metod NullPointerException
atacaq, əgər metoda yalnış arqumentlər ötürülsə — InvalidArgumentException
atacaq, əgər metodda təsadüfən sıfıra bölmə olarsa — ArithmeticException
.
RuntimeException
sinifi
RuntimeException
— Exception
istisnalarının bir növüdür. Hətta deyə bilərik ki, RuntimeException
— adi istisnaların (Exception
) sadələşdirilmiş bir versiyasıdır: belə istisnalara daha az tələb və məhdudiyyətlər tətbiq edilir
Exception
və RuntimeException
arasındakı fərqi daha sonra öyrənəcəksiniz
2. Yoxlanılan istisnalar: throws
, checked exceptions
Javadakı istisnalar iki kateqoriyaya bölünür — yoxlanılan (checked) və yoxlanılmayan (unchecked).
Bütün RuntimeException
və Error
siniflərindən miras qalmış istisnalar unchecked-istisnalar sayılır, qalanları isə — checked-istisnalar.
Yoxlanılan istisnaların tətbiqindən 20 il keçdikdən sonra, demək olar ki, bütün Java proqramçıları bunun bir səhv olduğunu düşünürlər. Müasir məşhur framework-lərdəki bütün istisnaların 95%-i yoxlanılmayan istisnalardır. C# kimi dil, Java-nı demək olar ki, tamamilə kopyalamağı qərara aldı, lakin yenə də checked-istisnalar əlavə etmədi.
Bəs checked-istisnalar ilə unchecked arasında əsas fərq nədir?
Checked-istisnalara əlavə tələblər tətbiq olunur. Bu tələblər təxminən belədir:
Tələb 1
Əgər metod checked-istisna atırsa, bu istisnanın növü metodun başlığında qeyd olunmalıdır (metodun imzası). Beləliklə, həmin metodu çağıran bütün digər metodlar həmin metodun vacib bir istisna ata biləcəyini bilirlər.
Checked-istisnaların göstərilməsi metodun parametrlərindən sonra throws
açar sözündən sonra olur (bu throw
ilə qarışdırılmamalıdır). Bu təxminən belə görünür:
növ metod (parametrlər) throws istisna
Nümunə:
checked-istisna | unchecked-istisna |
---|---|
|
|
Sağdakı nümunədə kodumuz unchecked-istisna atır — heç bir əlavə əməliyyat tələb olunmur. Soldakı nümunədə metod checked-istisna atır, buna görə metodun imzasına throws
açar sözü əlavə edilib və istisna tipi göstərilib.
Əgər metod bir neçə checked-istisna atmağı planlaşdırırsa, onların hamısını throws
açar sözündən sonra vergüllə yazmaq lazımdır. Sıra vacib deyil. Nümunə:
public void calculate(int n) throws Exception, IOException
{
if (n == 0)
throw new Exception("n sıfıra bərabərdir!");
if (n == 1)
throw new IOException("n birə bərabərdir");
}
Tələb 2
Əgər imzasında checked-istisnalar olan bir metodu çağırırsınızsa, bu faktı ignor edə bilməzsiniz.
Bütün bu istisnaları catch
blokları əlavə edərək tutmalı, ya da onları metodunuzun throws
unda qeyd etməlisiniz.
Sanki özümüzə deyirik: bu istisnalar çox vacibdir, ona görə də onları mütləq tutmalıyıq. Əgər onları necə tutacağımızı bilməsək, metodumuzu çağıranlara xəbər verməliyik ki, belə istisnalar yarana bilər.
Nümunə:
Təsəvvür edin ki, insanlarla dolacaq bir dünya yaratmalı olan bir metod yazırıq. İnsanların başlanğıc sayı parametr kimi ötürülür. İnsanların sayı çox azdırsa, istisnaları əlavə etməliyik.
Dünyanı yaratmaq | Qeyd |
---|---|
|
Metod potensial olaraq iki checked-istisna ata bilər: |
- BosDunya
- TenhaDunya
Bu metodu çağırmağın 3 yolu var:
1. Yaradan istisnaları tutmuruq
Adətən belə olur ki, metoddakı vəziyyəti düzgün emal etmək məlum olmur.
Kod | Qeyd |
---|---|
|
Çağırıcı metod istisnaları tutmur və onların başqa metodlarla paylaşılması lazım olur: onları throws a əlavə edir |
2. Bəzi istisnaları tutmaq
Aydın səhvləri emal edirik, anlaşılmayanları - çağırıcı metoda göndəririk. Bunun üçün onların adlarını throws-ə əlavə etmək lazımdır:
Kod | Qeyd |
---|---|
|
Çağırıcı metod yalnız bir checked-istisna — TenhaDunya -ı tutur, digəri isə metodun imzasında əlavə olunmalıdır: throws -dan sonra |
3. Bütün istisnaları tuturuq
Əgər metod, çağırıcı metoda heç bir istisna geri qaytarmazsa, çağırıcı metod hər şeyin uğurla həyata keçdiyinə əmin olacaq. Ancaq vəziyyətin düzəldilməsi üçün uğursuz bir addım ata bilməyəcək.
Kod | Qeyd |
---|---|
|
Bu metodda bütün səhvlər tutulur. Çağırıcı metod hər şeyin uğurla həyata keçirildiyinə əmin olacaq. |
3. İstisnaların örtülməsi
Checked-istisnalar nəzəriyyə baxımından çox gözəl bir şey kimi görünürdü, amma praktikada tam bir xəyal qırıqlığı oldu.
Tutaq ki, sizin layihənizdə proqramın yüzlərlə yerindən çağırılan çox məşhur bir metodunuz var. Və siz bu metoda yeni bir checked-istisna əlavə etmək qərarına gəldiniz. Və çox yaxşı ola bilər ki, bu checked-istisna həqiqətən çox əhəmiyyətli və xüsusi olsun ki, yalnız main()
metodu bu istisnanı tutduğu zaman nə edəcəyini bilir.
Buna görə də sizə checked-istisna əlavə etmək lazım gələcək throws
-a bütün metodlara, hansı ki sizin məşhur metodunuzu çağırır. Həmçinin, həmin metodları çağıran metodlara da əlavə etmək lazım gələcək. Həmçinin, həmin metodları çağıran metodlara da.
Nəticədə sizin layihədəki metodların yarısında throws
-da yeni bir checked-istisna əlavə olunacaq. Daha sonra məlum olacaq ki, sizin layihəniz testlər ilə əhatə olunmuşdur və bu testlər artıq kompilyasiya olunmur. Sizə həmçinin testlərdəki throws
-ı da düzəltmək lazım gələcək.
Sonra isə bütün kodunuzu (yüzlərlə fayla düzəlişlər) digər proqramçılar nəzərdən keçirməli olacaqlar. Və burada özünüzə bir sual veririk: bəs layihəyə bu qədər dəyişikliyi hansı məqsədlə etdik? Bir-iki gün iş, sındırılmış testlər, və bütün bunlar bir checked-istisna əlavə etmək üçün?
Nəzərə alın ki, metodların miras alınması və təkrar müəyyənləşdirilməsi ilə bağlı da problemlər mövcuddur. Checked-istisnaların gətirdiyi problemlər onların faydasından çoxdur. Ümumiyyətlə, bu günlərdə çox az adam onları sevir və istifadə edir.
Amma yenə də çoxlu kod (o cümlədən Java standart kitabxanalarının kodu) bu checked-istisnaları ehtiva edir. Bəs bunlarla nə etmək lazımdır? Onları görməzdən gəlinə bilməz, onları necə idarə edəcəyimiz isə məlum deyil.
Java proqramçıları checked-istisnaları RuntimeException
-ın içinə «sarmağı» təklif etdilər. Başqa sözlə, bütün checked-istisnaları tutaraq, onların əvəzinə unchecked-istisnalar (məsələn, RuntimeException
) yaradıb və məhz onları atmaq. Bu, təxminən belə görünür:
try
{
burada checked-istisna meydana gələ bilər
}
catch(Exception exp)
{
throw new RuntimeException(exp);
}
Bu çox da gözəl bir həll deyil, amma heç də qəbuledilməz deyil: istisna sadəcə RuntimeException
-ın içinə qoyulub.
İstənilən zaman onu buradan asanlıqla çıxarmaq olar. Nümunə:
Kod | Qeyd |
---|---|
|
İstisnanı RuntimeException obyektinin içində alırıq. cause null ola bilər.Onun tipini müəyyənləşdiririk və onu checked tipli dəyişənə çevririk. |
4. Çoxlu istisna tutma
Proqramçılar kodun təkrarlanmasını çox sevmirlər. Bunun üçün hətta bir inkişaf prinsipi də yaradıblar — DRY: Don’t Repeat Yourself. Amma istisnaları emal edərkən tez-tez belə hallar olur ki, try
blokundan sonra eyni kodlu bir neçə catch
bloku gəlir.
Və ya məsələn, bir layihədə 3 catch
bloku bir cür kodda olur və başqa 2 catch
bloku başqa cür kodla. Bu, ümumi praktik vəziyyətdir, əgər sizin layihədə istisnaların emalına ciddi yanaşılırsa.
Java dilində 7-ci versiyadan başlayaraq bir catch
blokunda bir neçə istisna tipini göstərmək imkanı əlavə edilmişdir. Bu belə görünür:
try
{
səhvin baş verə biləcəyi kod
}
catch(Tipİstisna1 | Tipİstisna2 | Tipİstisna3 ad)
{
istisnaların emal kodu
}
catch
bloklarının sayı istənilən qədər ola bilər. Amma bir catch
blokunda bir-biri ilə miras əlaqəsi olan istisnaları göstərmək olmaz. Yəni, belə bir şey yazmaq olmaz: catch (Exception
| RuntimeException
e), çünki RuntimeException
sinifi Exception
-dan miras alır.
5. Öz istisnalarınız
Həmişə öz xüsusi istisna sinifinizi yarada bilərsiniz. Məsələn, RuntimeException
sinifindən irs alaraq. Bu təxminən belə görünəcək:
class SinifAdi extends RuntimeException
{
}
Detalları OOP, irsiyyət, konstruktorlar və metodların yenidən yazılmasını öyrəndiyiniz zaman müzakirə edəcəyik.
Ancaq, hətta belə sadə bir sinifiniz olsa belə – ümumiyyətlə kodsuz, yenə də onun istisnalarını ata bilərsiniz:
Kod | Qeyd |
---|---|
|
Unchecked istisna MyException atılır. |
Öz istisnalarınızla detallı işi Java Multithreading kvestində nəzərdən keçirəcəyik.
GO TO FULL VERSION