Salam! Mən bunu sizə bildirməyə nifrət edirəm, amma proqramçının işinin böyük bir hissəsi səhvlərlə məşğul olur. Və ən çox - özləri ilə. Elə olur ki, səhv etməyən insan yoxdur. Və belə proqramlar da yoxdur. Əlbəttə ki, səhv üzərində işləyərkən əsas şey onun səbəbini anlamaqdır. Və proqramda bunun üçün bir çox səbəb ola bilər. Bir anda Java yaradıcıları bir sualla qarşılaşdılar: proqramlardakı bu çox potensial səhvlərlə nə etməli? Onlardan tamamilə qaçmaq qeyri-realdır. Proqramçılar təsəvvür belə etmək mümkün olmayan bir şey yaza bilirlər :) Bu o deməkdir ki, dildə xətalarla mübarizə mexanizmi qurmaq lazımdır. Başqa sözlə, proqramda hansısa xəta baş veribsə, sonrakı iş üçün skript lazımdır. Səhv baş verdikdə proqram tam olaraq nə etməlidir? Bu gün biz bu mexanizmlə tanış olacağıq. Və buna "İstisnalar " deyilir .
Java-da istisna nədir
İstisna, proqramın işləməsi zamanı baş verən bəzi müstəsna, planlaşdırılmamış vəziyyətdir. Java-da bir çox istisna nümunələri ola bilər. Məsələn, siz fayldan mətni oxuyan və konsola ilk sətri göstərən kod yazdınız.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);
}
}
Ancaq belə bir fayl yoxdur! Proqramın nəticəsi istisna olacaq - FileNotFoundException
. Nəticə:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Hər bir istisna Java-da ayrıca siniflə təmsil olunur. Bütün istisna siniflər ümumi bir "əcdaddan" - ana sinifdən gəlir Throwable
. İstisna sinfinin adı adətən onun baş vermə səbəbini qısaca əks etdirir:
FileNotFoundException
(fayl tapılmadı)ArithmeticException
(riyazi əməliyyatı yerinə yetirərkən istisna)ArrayIndexOutOfBoundsException
(massiv xanasının sayı onun uzunluğundan artıq müəyyən edilir). Məsələn, 10 uzunluqlu massiv üçün xana massivi[23]-ni konsola çıxarmağa cəhd etsəniz.
Exception in thread "main"
Uh-uh :/ Heç nə aydın deyil. Bunun hansı səhv olduğu və haradan gəldiyi bəlli deyil. Faydalı məlumat yoxdur. Ancaq bu cür müxtəlif siniflər sayəsində proqramçı özü üçün əsas şeyi - sinfin adında olan səhv növü və onun ehtimal olunan səbəbini alır. Axı, konsolda görmək tamamilə fərqli bir şeydir:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Problemin nə ola biləcəyi və problemi həll etmək üçün "hansı istiqamətdə qazılacağı" dərhal aydın olur! İstisnalar, hər hansı sinif nümunələri kimi, obyektlərdir.
Tutma və İstisnalar
Java-da istisnalarla işləmək üçün xüsusi kod blokları mövcuddur:try
, catch
və finally
. Proqramçının istisnaların baş verəcəyini gözlədiyi kod blokda yerləşdirilir try
. Bu o demək deyil ki, bu yerdə mütləq istisna baş verəcək. Bu o deməkdir ki, orada baş verə bilər və proqramçı bundan xəbərdardır. Qəbul etməyi gözlədiyiniz xətanın növü blokda catch
(“tutmaq”) yerləşdirilir. İstisna baş verərsə, icra edilməli olan bütün kodlar da burada yerləşdirilir. Budur bir nümunə:
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!");
}
}
Nəticə:
Ошибка! Файл не найден!
Kodumuzu iki bloka yerləşdiririk. Birinci blokda “Fayl tapılmadı” xətasının baş verə biləcəyini gözləyirik. Bu blokdur try
. İkincidə, proqrama bir səhv baş verərsə, nə edəcəyini söyləyirik. Üstəlik, müəyyən bir səhv növü var - FileNotFoundException
. Başqa bir istisna sinifini blok mötərizələrinə keçirsək catch
, tutulmayacaq.
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!");
}
}
Nəticə:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Blokdakı kod catch
işləmədi, çünki biz bu bloku kəsmək üçün “konfiqurasiya etdik” ArithmeticException
və blokdakı kod try
başqa bir növü atdı - FileNotFoundException
. Biz skript yazmadıq FileNotFoundException
, ona görə də proqram konsolda standart olaraq üçün göstərilən məlumatları göstərdi FileNotFoundException
. Burada 3 şeyə diqqət yetirmək lazımdır. Birinci. Sınaq blokunda hər hansı kod sətirində istisna baş verən kimi ondan sonrakı kod artıq icra olunmayacaq. Proqramın icrası dərhal bloka "atılacaq" catch
. Misal üçün:
public static void main(String[] args) {
try {
System.out.println("Divide a number 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("Not");
System.out.println("will");
System.out.println("done!");
} catch (ArithmeticException e) {
System.out.println("The program jumped to the catch block!");
System.out.println("Error! You can't divide by zero!");
}
}
Nəticə:
Делим число на ноль
Программа перепрыгнула в блок catch!
Ошибка! Нельзя делить на ноль!
İkinci sətirdəki blokda try
bir ədədi 0-a bölməyə çalışdıq, bu da istisna ilə nəticələndi ArithmeticException
. Bundan sonra blokun 6-10-cu sətirləri try
artıq icra olunmayacaq. Dediyimiz kimi, proqram dərhal bloku icra etməyə başladı catch
. İkinci. Bir neçə blok ola bilər catch
. Bir blokdakı kod try
bir deyil, bir neçə növ istisna ata bilərsə, onların hər biri üçün öz blokunuzu yaza bilərsiniz catch
.
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!");
}
}
Bu nümunədə iki blok yazdıq catch
. Əgər blokda try
baş verərsə FileNotFoundException
, birinci blok icra olunacaq catch
. Əgər baş verərsə ArithmeticException
, ikincisi edam olunacaq. Ən azı 50 blok yaza bilərsiniz.Amma catch
təbii ki, 50 müxtəlif növ xəta verə biləcək kod yazmamaq daha yaxşıdır :) Üçüncüsü. Kodunuzun hansı istisnalara səbəb ola biləcəyini necə bilirsiniz? Yaxşı, əlbəttə ki, bəziləri haqqında təxmin edə bilərsiniz, amma hər şeyi başınızda saxlamaq mümkün deyil. Buna görə də Java kompilyatoru ən ümumi istisnaları bilir və onların hansı hallarda baş verə biləcəyini bilir. Məsələn, kodu yazmısınızsa və kompilyator onun işləməsi zamanı 2 növ istisnanın baş verə biləcəyini bilirsə, siz onları idarə edənə qədər kodunuz tərtib edilməyəcək. Bunun nümunələrini aşağıda görəcəyik. İndi istisnaların idarə olunması ilə bağlı. Onları emal etməyin 2 yolu var. Biz artıq birincisi ilə tanış olduq - metod blokda istisnanı müstəqil şəkildə idarə edə bilər catch()
. İkinci bir seçim var - metod zəng yığınına bir istisna ata bilər. Bunun mənası nədi? printFirstString()
Məsələn, bizim sinifimizdə faylı oxuyan və konsolda ilk sətrini göstərən bir üsul var - eyni üsul :
public static void printFirstString(String filePath) {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
Hazırda kodumuz tərtib edilmir, çünki işlənməmiş istisnalar var. 1-ci sətirdə faylın yolunu göstərirsiniz. Kompilyator bilir ki, belə kod asanlıqla FileNotFoundException
. 3-cü sətirdə siz fayldan mətn oxuyursunuz. IOException
Bu prosesdə giriş-çıxış (Giriş-Çıxış) zamanı xəta asanlıqla baş verə bilər . İndi tərtibçi sizə deyir: “Dostum, siz mənə bu istisnalardan biri baş verərsə, nə etməli olduğumu söyləməyincə mən bu kodu təsdiq etməyəcəyəm və ya tərtib etməyəcəyəm. Və onlar mütləq yazdığınız kod əsasında baş verə bilər!” . Getmək üçün heç bir yer yoxdur, hər ikisini emal etməlisiniz! Birinci emal seçimi artıq bizə tanışdır: kodumuzu bloka yerləşdirməli try
və iki blok əlavə etməliyik catch
:
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("Error while inputting/outputting data from file!");
e.printStackTrace();
}
}
Ancaq bu yeganə seçim deyil. Metod daxilində səhv üçün skript yazmaqdan qaça bilərik və sadəcə olaraq istisnanı yuxarıya atırıq. throws
Bu , metod bəyannaməsində yazılmış açar sözdən istifadə etməklə edilir :
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
Sözdən sonra, throws
bu metodun əməliyyat zamanı ata biləcəyi istisnaların bütün növlərini vergüllə ayırırıq. Bu niyə edilir? İndi, proqramda kimsə metodu çağırmaq istəsə printFirstString()
, o, istisnalarla işləməyi özü həyata keçirməli olacaq. Məsələn, proqramın başqa bir hissəsində həmkarlarınızdan biri sizin metodunuzu çağırdığı bir üsul yazdı printFirstString()
:
public static void yourColleagueMethod() {
//...your colleague's method does something
//...and at one moment calls your printFirstString() method with the file it needs
printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Xəta, kod tərtib edilmir! Metodda printFirstString()
səhvlərin idarə edilməsi skripti yazmadıq . Ona görə də vəzifə bu üsuldan istifadə edəcəklərin çiyninə düşür. Yəni, üsul yourColleagueMethod()
indi eyni 2 variantla üzləşir: ya istifadə edərək ona “uçmuş” hər iki istisnanı emal etməlidir try-catch
, ya da onları daha da irəli sürməlidir.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
//...the method does something
//...and at one moment calls your printFirstString() method with the file it needs
printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
İkinci halda, emal yığındakı növbəti metodun çiyinlərinə düşəcək - zəng edəcək olan yourColleagueMethod()
. Buna görə belə bir mexanizm "istisnanı yuxarıya atmaq" və ya "yuxarıya keçmək" adlanır. İstisnaları istifadə edərkən throws
, kod tərtib edilir. Bu anda tərtibçi deyəsən: “Yaxşı, tamam. Sizin kodunuz bir sıra potensial istisnaları ehtiva edir, amma hər halda onu tərtib edəcəyəm. Biz bu söhbətə qayıdacağıq!” Proqramın hardasa istisnalarını idarə etməmiş metodu çağırdığınız zaman kompilyator vədi yerinə yetirir və onlar haqqında sizə bir daha xatırladır. Nəhayət, blok haqqında danışacağıq finally
(bağışlayın). Bu, triumvirate ilə bağlı istisnaların son hissəsidir try-catch-finally
. Onun özəlliyi ondan ibarətdir ki, o, istənilən proqramın əməliyyat ssenarisi üzrə icra olunur.
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 is the finally block!");
}
}
Bu misalda blok daxilindəki kod finally
hər iki halda icra olunur. Blokdakı kod try
tamamilə yerinə yetirilirsə və istisna yaratmırsa, blok sonunda işə düşəcək finally
. Əgər daxilindəki kod try
kəsilirsə və proqram bloka sıçrayırsa catch
, daxildəki kod yerinə yetirildikdən sonra catch
blok yenə də seçiləcək finally
. Niyə lazımdır? Onun əsas məqsədi kodun tələb olunan hissəsini icra etməkdir; şəraitdən asılı olmayaraq tamamlanmalı olan hissə. Məsələn, tez-tez proqram tərəfindən istifadə olunan bəzi resursları boşaldır. Kodumuzda biz fayldan məlumatı oxumaq və onu BufferedReader
. Bizimkini reader
bağlamaq və resursları boşaltmaq lazımdır. Bu, hər halda edilməlidir: proqramın gözlənildiyi kimi işləməsi və ya istisna təşkil etməsinin əhəmiyyəti yoxdur. Bunu bir blokda etmək rahatdır finally
:
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 is the finally block!");
if (reader != null) {
reader.close();
}
}
}
İndi tam əminik ki, proqram işləyərkən nə baş verməsindən asılı olmayaraq, işğal olunmuş resursların qayğısına qalmışıq :) İstisnalar haqqında bilmək lazım olan hər şey bu deyil. Səhvlərin həlli proqramlaşdırmada çox vacib bir mövzudur: birdən çox məqalə ona həsr edilmişdir. Növbəti dərsdə hansı növ istisnaların olduğunu və öz istisnanızı necə yaratacağınızı öyrənəcəyik :) Orada görüşənədək!
GO TO FULL VERSION