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.

🕑 15 daqiqa o‘qish 📄 734 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. try va catch
  2. Istisnolar iyerarxiyasi
  3. Tekshiriladigan istisnolar
  4. Qaysi turni tanlash
  5. Bir nechta catch
  6. finally
  7. try-with-resources
  8. O'z istisnongiz
  9. Sababni saqlash
  10. Nima qilmaslik kerak
  11. Xulosa

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 #

Java
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");
    }
}
Natija
Xato: / by zero
Dastur davom etmoqda

Istisnolar iyerarxiyasi #

Throwable va uning avlodlari Throwable Error ushlamang Exception ishlaysiz OutOfMemoryError StackOverflowError Tekshiriladigan IOException, SQLException RuntimeException NullPointer, Arithmetic Tekshiriladigan istisnoni kompilyator majburan ushlatadi yoki `throws` yozdiradi. RuntimeException avlodlarida bunday majburiyat yo'q - ular odatda dasturchi xatosi. Bu ajratish faqat Java'da bor - C#, Python va boshqa tillarda yo'q.
Tekshiriladigan istisnolar - Java'ning o'ziga xos xususiyati

Tekshiriladigan istisnolar #

Java
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());
        }
    }
}
Natija
Ushlandi: fayl o'qilmadi
Ushlandi: bu tekshirilmaydi
Tekshiriladigan istisnoni e'tiborsiz qoldirib bo'lmaydi

xavfli metodi throws IOException deb e'lon qilingan. Uni chaqirgan kod majburan ikkitadan birini qilishi kerak:

  1. try/catch bilan ushlash;
  2. O'zi ham throws IOException deb e'lon qilish.

Aks holda kod kompilyatsiya qilinmaydi:

Natija
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 #

HolatTur
Tashqi sabab, chaqiruvchi tuzata oladiTekshiriladigan (IOException)
Dasturchi xatosiRuntimeException
Argument noto'g'riIllegalArgumentException
Obyekt hozir bunga tayyor emasIllegalStateException
Qiymat nullNullPointerException
Zamonaviy amaliyot

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 #

Java
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");
            }
        }
    }
}
Natija
'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:

Java
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());
            }
        }
    }
}
Natija
Ushlandi: NumberFormatException
Ushlandi: ArrayIndexOutOfBoundsException
Tartib muhim: avlod avval
Java
try {
    // ...
} catch (Exception x) {          // eng keng
} catch (IOException x) {         // XATO - hech qachon yetib bormaydi
}
Natija
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 #

Java
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");
            }
        }
    }
}
Natija
[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 yozmang
Java
public 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
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:

Java
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");
    }
}
Natija
baza ochildi
fayl ochildi
baza ishlatilmoqda
fayl ishlatilmoqda
fayl yopildi
baza yopildi
---
tarmoq ochildi
tarmoq ishlatilmoqda
tarmoq yopildi
Ushlandi: xato yuz berdi

Diqqat qiling:

  1. Resurslar teskari tartibda yopildi - oxirgi ochilgani birinchi;
  2. Istisno bo'lganda ham close() chaqirildi va u catch dan oldin bajarildi.
AutoCloseable bo'lsa - har doim try-with-resources

Eski uslub:

Java
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 #

Java
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;
    }
}
Natija
Ushlandi: Manfiy qiymat qabul qilinmaydi: -5
Xato qiymat: -5

Sababni saqlash #

Java
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);
        }
    }
}
Natija
Xabar:  Sozlama o'qilmadi
Sababi: For input string: "abc"
Sabab turi: NumberFormatException
Asl istisnoni yo'qotmang
Java
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 #

Bo'sh catch - eng yomon amaliyot
Java
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 ushlamang

catch (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.

Amaliy topshiriq
  1. Nolga bo'lishni try/catch bilan ushlang.
  2. throws IOException li metod yozib, uni ushlamasdan chaqiring.
  3. Tekshiriladigan va tekshirilmaydigan istisno farqini yozing.
  4. Bir nechta turni bitta catch da birlashtiring.
  5. catch tartibini buzib, kompilyator nima deyishini ko'ring.
  6. finally blokining har doim bajarilishini tekshiring.
  7. finally ichida return yozib, natijani ko'ring.
  8. AutoCloseable sinf yozib, try-with-resources bilan ishlating.
  9. O'z istisno turingizni yarating va qo'shimcha ma'lumot qo'shing.
  10. Istisnoni sabab bilan qayta tashlang va getCause() ni tekshiring.

Xulosa #

  • Java'da istisnolar ikki turga bo'linadi: tekshiriladigan va RuntimeException avlodlari.
  • Tekshiriladigan istisnoni ushlash yoki throws bilan e'lon qilish majburiy.
  • O'z kodingizda ko'pincha RuntimeException avlodini tanlang.
  • catch bloklarida torroq turlar yuqorida bo'lishi shart.
  • finally har doim bajariladi; ichida return yozmang - u istisnoni yutib yuboradi.
  • try-with-resources resurslarni teskari tartibda va har qanday holatda yopadi.
  • Qayta tashlaganda asl istisnoni sabab sifatida uzating.
  • Bo'sh catch va catch (Exception) - eng ko'p zarar keltiradigan ikki odat.

Keyingi bo'limda generiklarni o'rganamiz.

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.