13-bo‘lim
Istisnolar
try, catch va finally, tekshiriladigan va tekshirilmaydigan istisnolar farqi, try-with-resources, o'z istisnongiz va istisnolarni to'g'ri ishlatish qoidalari.
Ushbu bo‘lim mundarijasi
Istisno - dastur davom eta olmaydigan holat: fayl topilmadi, tarmoq uzildi, foydalanuvchi noto'g'ri ma'lumot berdi.
Java'da istisnolar boshqa ko'p tillardan jiddiy farq qiladi va bu farqni tushunish muhim.
try va catch #
public class Birinchi {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
System.out.println(a / b);
} catch (ArithmeticException x) {
System.out.println("Xato: " + x.getMessage());
}
System.out.println("Dastur davom etmoqda");
}
}
Xato: / by zero
Dastur davom etmoqda
Istisnolar iyerarxiyasi #
Tekshiriladigan istisnolar #
import java.io.IOException;
public class Tekshiriladigan {
public static void main(String[] args) {
try {
xavfli(true);
} catch (IOException x) {
System.out.println("Ushlandi: " + x.getMessage());
}
oddiy();
}
static void xavfli(boolean xatoBersin) throws IOException {
if (xatoBersin) {
throw new IOException("fayl o'qilmadi");
}
}
static void oddiy() {
try {
throw new IllegalStateException("bu tekshirilmaydi");
} catch (IllegalStateException x) {
System.out.println("Ushlandi: " + x.getMessage());
}
}
}
Ushlandi: fayl o'qilmadi
Ushlandi: bu tekshirilmaydi
xavfli metodi throws IOException deb e'lon qilingan. Uni
chaqirgan kod majburan ikkitadan birini qilishi kerak:
try/catchbilan ushlash;- O'zi ham
throws IOExceptiondeb e'lon qilish.
Aks holda kod kompilyatsiya qilinmaydi:
error: unreported exception IOException;
must be caught or declared to be thrown
IllegalStateException esa RuntimeException avlodi - unda bunday
majburiyat yo'q.
Bu Java'ning eng ko'p bahs qilinadigan qarorlaridan biri. G'oya
yaxshi edi: dasturchi xatoni e'tiborsiz qoldirmasin. Amalda esa bu
ko'pincha bo'sh catch bloklariga olib keldi - bu esa hech
narsadan yomonroq.
Qaysi turni tanlash #
| Holat | Tur |
|---|---|
| Tashqi sabab, chaqiruvchi tuzata oladi | Tekshiriladigan (IOException) |
| Dasturchi xatosi | RuntimeException |
| Argument noto'g'ri | IllegalArgumentException |
| Obyekt hozir bunga tayyor emas | IllegalStateException |
Qiymat null | NullPointerException |
Ko'p yangi kutubxonalar (Spring ham) tekshiriladigan istisnolardan
qochadi va hamma narsani RuntimeException qiladi.
Sababi: tekshiriladigan istisno butun chaqiruvlar zanjirini
"iflos" qiladi - har bir metod throws yozishga majbur bo'ladi.
O'z kodingizda: ko'p hollarda RuntimeException avlodini
tanlang. Tekshiriladigan istisnoni faqat chaqiruvchi haqiqatan
tuzata oladigan holatlar uchun qoldiring.
Bir nechta catch #
public class KopCatch {
public static void main(String[] args) {
String[] kirishlar = {"42", "abc", null};
for (String kirish : kirishlar) {
try {
System.out.println("'" + kirish + "' -> "
+ Integer.parseInt(kirish));
} catch (NumberFormatException x) {
System.out.println("'" + kirish + "' -> raqam emas");
}
}
}
}
'42' -> 42
'abc' -> raqam emas
'null' -> raqam emas
Diqqat: null ham NumberFormatException berdi -
NullPointerException emas. Integer.parseInt(null) shunday
ishlaydi.
Bir nechta turni bitta blokda ushlash mumkin:
public class Birlashtirilgan {
public static void main(String[] args) {
for (int holat = 0; holat < 2; holat++) {
try {
if (holat == 0) {
Integer.parseInt("abc");
} else {
int[] m = new int[1];
System.out.println(m[5]);
}
} catch (NumberFormatException
| ArrayIndexOutOfBoundsException x) {
System.out.println("Ushlandi: "
+ x.getClass().getSimpleName());
}
}
}
}
Ushlandi: NumberFormatException
Ushlandi: ArrayIndexOutOfBoundsException
try {
// ...
} catch (Exception x) { // eng keng
} catch (IOException x) { // XATO - hech qachon yetib bormaydi
}
error: exception IOException has already been caught
catch bloklari yuqoridan pastga tekshiriladi. Kengroq turni
yuqoriga qo'ysangiz, torroq turlar hech qachon ishlamaydi va
kompilyator buni xato deb hisoblaydi.
Torroq turlarni yuqoriga qo'ying.
finally #
public class Finally {
public static void main(String[] args) {
for (String holat : new String[]{"yaxshi", "yomon"}) {
try {
System.out.println("[" + holat + "] boshlandi");
if (holat.equals("yomon")) {
throw new IllegalStateException("xato");
}
System.out.println("[" + holat + "] muvaffaqiyatli");
} catch (IllegalStateException x) {
System.out.println("[" + holat + "] ushlandi: "
+ x.getMessage());
} finally {
System.out.println("[" + holat + "] tozalash bajarildi");
}
}
}
}
[yaxshi] boshlandi
[yaxshi] muvaffaqiyatli
[yaxshi] tozalash bajarildi
[yomon] boshlandi
[yomon] ushlandi: xato
[yomon] tozalash bajarildi
finally har doim bajariladi - istisno bo'ldimi yoki yo'qmi.
finally ichida return yozmangpublic class FinallyReturn {
public static void main(String[] args) {
System.out.println("Natija: " + yomon());
}
@SuppressWarnings("finally")
static int yomon() {
try {
return 1;
} finally {
return 2; // birinchi return'ni yutib yuboradi
}
}
}
Natija: 2
try bloki 1 qaytarmoqchi edi, lekin finally dagi return uni
almashtirdi.
Bundan ham yomoni: try da istisno tashlansa-yu, finally da
return bo'lsa, istisno butunlay yo'qoladi va hech kim u haqda
bilmaydi.
Kompilyator bu haqda ogohlantiradi. finally ichida hech qachon
return, break yoki continue yozmang.
try-with-resources #
Fayl va tarmoq ulanishlari ishlatilgandan keyin yopilishi kerak:
public class Resurslar {
public static void main(String[] args) {
try (Manba a = new Manba("baza");
Manba b = new Manba("fayl")) {
a.ishlat();
b.ishlat();
}
System.out.println("---");
try (Manba c = new Manba("tarmoq")) {
c.ishlat();
throw new IllegalStateException("xato yuz berdi");
} catch (IllegalStateException x) {
System.out.println("Ushlandi: " + x.getMessage());
}
}
}
class Manba implements AutoCloseable {
private final String nom;
Manba(String nom) {
this.nom = nom;
System.out.println(nom + " ochildi");
}
void ishlat() {
System.out.println(nom + " ishlatilmoqda");
}
@Override
public void close() {
System.out.println(nom + " yopildi");
}
}
baza ochildi
fayl ochildi
baza ishlatilmoqda
fayl ishlatilmoqda
fayl yopildi
baza yopildi
---
tarmoq ochildi
tarmoq ishlatilmoqda
tarmoq yopildi
Ushlandi: xato yuz berdi
Diqqat qiling:
- Resurslar teskari tartibda yopildi - oxirgi ochilgani birinchi;
- Istisno bo'lganda ham
close()chaqirildi va ucatchdan oldin bajarildi.
AutoCloseable bo'lsa - har doim try-with-resourcesEski uslub:
Manba m = new Manba("fayl");
try {
m.ishlat();
} finally {
m.close();
}
Yangi uslub qisqaroq, xatoga kamroq moyil va bir nechta resursni to'g'ri tartibda yopadi.
Files, Scanner, InputStream, JDBC ulanishlari - hammasi
AutoCloseable. Ularni har doim shu shaklda ishlating.
O'z istisnongiz #
public class OzIstisno {
public static void main(String[] args) {
try {
yechish(-5);
} catch (SalbiyQiymatException x) {
System.out.println("Ushlandi: " + x.getMessage());
System.out.println("Xato qiymat: " + x.qiymat());
}
}
static void yechish(int son) {
if (son < 0) {
throw new SalbiyQiymatException(son);
}
System.out.println(Math.sqrt(son));
}
}
class SalbiyQiymatException extends RuntimeException {
private final int qiymat;
SalbiyQiymatException(int qiymat) {
super("Manfiy qiymat qabul qilinmaydi: " + qiymat);
this.qiymat = qiymat;
}
int qiymat() {
return qiymat;
}
}
Ushlandi: Manfiy qiymat qabul qilinmaydi: -5
Xato qiymat: -5
Sababni saqlash #
public class Sabab {
public static void main(String[] args) {
try {
yuqoriDaraja();
} catch (IllegalStateException x) {
System.out.println("Xabar: " + x.getMessage());
System.out.println("Sababi: " + x.getCause().getMessage());
System.out.println("Sabab turi: "
+ x.getCause().getClass().getSimpleName());
}
}
static void yuqoriDaraja() {
try {
Integer.parseInt("abc");
} catch (NumberFormatException x) {
throw new IllegalStateException("Sozlama o'qilmadi", x);
}
}
}
Xabar: Sozlama o'qilmadi
Sababi: For input string: "abc"
Sabab turi: NumberFormatException
catch (NumberFormatException x) {
throw new IllegalStateException("Sozlama o'qilmadi"); // YOMON
}
Bu variantda asl xato butunlay yo'qoladi va jurnalda faqat "Sozlama o'qilmadi" qoladi - qaysi sozlama, nima uchun, qayerda - hech narsa ma'lum emas.
Ikkinchi argument sifatida asl istisnoni uzating - u
getCause() orqali saqlanadi va stek izida to'liq ko'rinadi.
Nima qilmaslik kerak #
try {
// biror ish
} catch (Exception x) {
// hech narsa
}
Bu xatoni yashiradi: dastur noto'g'ri ishlaydi, lekin siz bilmaysiz. Nosozlikni topish deyarli imkonsiz bo'lib qoladi.
Java'da bu ayniqsa keng tarqalgan, chunki tekshiriladigan istisnolar
dasturchini catch yozishga majbur qiladi - va eng oson yo'l
uni bo'sh qoldirish.
Agar istisnoni haqiqatan e'tiborsiz qoldirish kerak bo'lsa, nega shunday qilinayotganini izohda yozing.
Exception bilan hammasini ushlamangcatch (Exception x) NullPointerException va boshqa dasturchi
xatolarini ham ushlaydi - ya'ni tuzatilishi kerak bo'lgan
xatolarni yashiradi.
Faqat qanday ishlashni bilgan istisnolaringizni ushlang.
catch (Throwable x) esa bundan ham yomon: u OutOfMemoryError va
StackOverflowError ni ham ushlaydi, ularni esa ushlab hech narsa
qilib bo'lmaydi.
- Nolga bo'lishni
try/catchbilan ushlang. throws IOExceptionli metod yozib, uni ushlamasdan chaqiring.- Tekshiriladigan va tekshirilmaydigan istisno farqini yozing.
- Bir nechta turni bitta
catchda birlashtiring. catchtartibini buzib, kompilyator nima deyishini ko'ring.finallyblokining har doim bajarilishini tekshiring.finallyichidareturnyozib, natijani ko'ring.AutoCloseablesinf yozib,try-with-resourcesbilan ishlating.- O'z istisno turingizni yarating va qo'shimcha ma'lumot qo'shing.
- Istisnoni sabab bilan qayta tashlang va
getCause()ni tekshiring.
Xulosa #
- Java'da istisnolar ikki turga bo'linadi: tekshiriladigan va
RuntimeExceptionavlodlari. - Tekshiriladigan istisnoni ushlash yoki
throwsbilan e'lon qilish majburiy. - O'z kodingizda ko'pincha
RuntimeExceptionavlodini tanlang. catchbloklarida torroq turlar yuqorida bo'lishi shart.finallyhar doim bajariladi; ichidareturnyozmang - u istisnoni yutib yuboradi.try-with-resourcesresurslarni teskari tartibda va har qanday holatda yopadi.- Qayta tashlaganda asl istisnoni sabab sifatida uzating.
- Bo'sh
catchvacatch (Exception)- eng ko'p zarar keltiradigan ikki odat.
Keyingi bo'limda generiklarni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.