İçeriğe geç
Educora
İleri17 dk10 / 18

İstisnalar: hataları yönetmek

try/catch/finally ile hataları yakala, denetlenen ve denetlenmeyen istisnaları ayırt et, throw ile istisna fırlat ve kendi istisna sınıfını oluştur.

Kendini test et
Bu derste öğreneceklerin
  • İstisnanın ne olduğunu açıklamak ve yığın izini okumak
  • try, catch ve finally bloklarını kullanmak
  • Denetlenen ve denetlenmeyen istisnaları ayırt etmek ve özel bir istisna sınıfı yazmak

Kullanıcı yaşı yerine “abc” yazdı, bir dosya silindi, internet kesildi... Gerçek programlarda her şey plana göre gitmez. İyi bir program böyle durumlarda “çökmez”; sorunu kibarca bildirir ve çalışmaya devam eder. Java'da beklenmedik durumlar istisnalarla (exceptions) yönetilir.

İstisna nedir?

İstisna, programın normal akışını bozan bir olayı tanımlayan bir nesnedir. Böyle bir olay gerçekleştiğinde Java istisnayı “fırlatır” (throw): o anki metot hemen durur ve istisna, onu yakalayan bir kod bulunana kadar çağrı zinciri boyunca yukarı çıkar. Kimse yakalamazsa program durur ve konsola bir yığın izi (stack trace) yazdırılır.

Java
public class Main {
    public static void main(String[] args) {
        int[] marks = {5, 4, 3};
        System.out.println("Start");
        System.out.println(marks[5]);
        System.out.println("End");
    }
}
Beklenen çıktı
Start
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
	at Main.main(Main.java:5)

Yığın izini yukarıdan aşağıya oku: önce istisnanın türü (ArrayIndexOutOfBoundsException), sonra mesajı (5. indeks, 3 uzunluğundaki dizinin dışında), ardından nerede gerçekleştiği: Main.java dosyasının 5. satırı. “End” yazdırılmadı: bir istisnadan sonra metodun geri kalanı çalışmaz.

try, catch ve finally

Riskli kodu bir try bloğuna koyarız. Bir istisna oluşursa kontrol, uygun türdeki catch bloğuna geçer. finally bloğu ise her durumda, istisna olsa da olmasa da çalışır; genellikle bir dosyayı ya da bağlantıyı kapatmak gibi “toparlama” işleri için kullanılır.

Java
public class Main {
    public static void main(String[] args) {
        String[] inputs = {"42", "abc", "0"};
        for (String input : inputs) {
            try {
                int n = Integer.parseInt(input);
                System.out.println("100 / " + n + " = " + (100 / n));
            } catch (NumberFormatException e) {
                System.out.println("Not a number: " + e.getMessage());
            } catch (ArithmeticException e) {
                System.out.println("Math error: " + e.getMessage());
            } finally {
                System.out.println("-- checked \"" + input + "\"");
            }
        }
    }
}
Beklenen çıktı
100 / 42 = 2
-- checked "42"
Not a number: For input string: "abc"
-- checked "abc"
Math error: / by zero
-- checked "0"
  • Integer.parseInt("abc") metni sayıya çeviremez ve NumberFormatException fırlatır.
  • Bir tam sayıyı sıfıra bölmek ArithmeticException oluşturur. (İlginçtir ki 100.0 / 0 istisna fırlatmaz ve Infinity verir.)
  • e.getMessage() istisnanın mesajını döndürür. Program istisnadan sonra durmadı; döngü bir sonraki değerle devam etti.
  • finally üç durumda da çalıştı.

Denetlenen ve denetlenmeyen istisnalar

Tüm istisnaların ortak atası Throwable'dır. Ondan Error (belleğin tükenmesi gibi ciddi JVM sorunları; bunlar genellikle yakalanmaz) ve Exception türer. Exception'ın da özel bir kolu vardır: RuntimeException. Bu ayrım, Java'ya özgü bir kuralı belirler:

ÖzellikDenetlenen (checked)Denetlenmeyen (unchecked)
Türediği sınıfException (ama RuntimeException değil)RuntimeException
Derleyici zorunlu tutar mı?evet: yakala ya da throws ile bildirhayır
Genellikle anlamıprogramın denetimi dışında bir sorunbir programlama hatası
ÖrneklerIOException, FileNotFoundExceptionNullPointerException, ArithmeticException, IllegalArgumentException

Örneğin new FileReader("data.txt") yazıp onu try içine almazsan kod derlenmez: unreported exception FileNotFoundException; must be caught or declared to be thrown. Derleyici sanki şöyle der: “Dosya olmayabilir; bu durumda ne yapacağına önceden karar ver.”

throw, throws ve kendi istisnaların

Bir istisnayı kendin de fırlatabilirsin: throw new IllegalArgumentException("..."). Bu, bir metodun hatalı veri kabul etmesini önlemenin en iyi yoludur. Kendi istisna sınıfını oluşturmak için Exception (denetlenen) ya da RuntimeException (denetlenmeyen) sınıfından türet. Denetlenen bir istisna fırlatan metot bunu başlığında throws ile bildirmelidir.

Java
public class Main {
    static void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Invalid age: " + age);
        }
        System.out.println("Age set to " + age);
    }

    public static void main(String[] args) {
        try {
            setAge(25);
            setAge(-3);
            setAge(40);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        System.out.println("Program continues");
    }
}
Beklenen çıktı
Age set to 25
Error: Invalid age: -3
Program continues

setAge(40) hiç çağrılmadı: setAge(-3) istisna fırlatır fırlatmaz try bloğu durdu ve kontrol catch'e geçti. Şimdi de kendi denetlenen istisnamızı oluşturalım; cüzdanda yeterli para olmadığı durum için:

Java
public class Main {
    public static void main(String[] args) {
        Wallet wallet = new Wallet(30);
        try {
            wallet.pay(12);
            wallet.pay(25);
        } catch (InsufficientFundsException e) {
            System.out.println(e.getMessage());
            System.out.println("Missing: " + e.getMissing() + " AZN");
        }
    }
}

class InsufficientFundsException extends Exception {
    private final double missing;

    InsufficientFundsException(double missing) {
        super("Not enough money in the wallet");
        this.missing = missing;
    }

    double getMissing() { return missing; }
}

class Wallet {
    private double balance;

    Wallet(double balance) { this.balance = balance; }

    void pay(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException(amount - balance);
        }
        balance -= amount;
        System.out.println("Paid " + amount + ", left " + balance);
    }
}
Beklenen çıktı
Paid 12.0, left 18.0
Not enough money in the wallet
Missing: 7.0 AZN

Önemli noktalar

  • İstisna, normal akışı bozan bir olaydır; yakalanmazsa program durur ve yığın izi yazdırılır.
  • try riskli kodu içerir, catch belirli türde bir istisnayı yakalar, finally ise her zaman çalışır.
  • Denetlenen istisnalar (IOException) yakalanmalı ya da throws ile bildirilmelidir; RuntimeException alt sınıfları denetlenmez.
  • throw new ... bir istisna fırlatır; kendi istisnan için Exception ya da RuntimeException'dan türet.
  • Boş bir catch bloğu yazma; hatayla ilgili bilgi kaybolur.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
finally bloğu ne zaman çalışır?