---
title: 'İstisna Hiyerarşisi'
source: 'https://academia.sh/tr/kurslar/java-nesne-modeli/istisna-hiyerarsisi'
course: 'Nesneye Dayalı Java'
language: tr
updated: '2026-08-17T18:09:41+00:00'
license: 'CC BY-SA 4.0'
---

# İstisna Hiyerarşisi

Denetlenen ile denetlenmeyen istisna ayrımının derleme zamanında ne zorladığı ölçülür: dokuz yazımın yalnız biri reddedilir, bildirim sınıf dosyasında komut değil öznitelik olarak durur ve sanal makine onu denetlemez. Yakalamanın hangi dalı çalıştıracağına ise çalışma zamanı tipi karar verir.

Önceki iki ders derleyicinin kapattığı kapıları saydı: yanlış tipe yazma derlenmedi,
kovaryant görünüme yazma derlenmedi, kaptan okunan değeri aynı kaba geri yazmak derlenmedi.
Hepsinde derleyici bir **yasak** koydu. Java'da derleyicinin kaynağa dayattığı bir de
**zorunluluk** vardır: kimi olaylar bildirilmeden ya da işlenmeden bir yöntem yazılamaz.

İstisnanın bir nesne olduğu, yakalamanın bir sınıf aldığı ve hiyerarşinin kaç olayı tuttuğu
Python Temelleri kursunda ölçüldü; o aritmetik burada tekrarlanmıyor. Buradaki soru kursun
sorusudur ve iki yanıtı vardır: bir istisnayı **bildirme** zorunluluğuna bildirilen tip karar
verir, **hangi yakalama dalının çalışacağına** ise çalışma zamanı tipi. Bu ders ikisini de
aynı yerde ölçer.

## Derleyicinin Zorladığı Tek Şey

- **GN15** — Üç istisna türü kaynakta dersin içinde tanımlanır: `Exception` altında bir
  denetlenen istisna, `RuntimeException` altında bir denetlenmeyen istisna, `Error` altında
  bir ağır hata.
- **GN16** — Üç yazım biçimi aynı gövdeyi taşır; yalnız yöntemin bildirimi ve gövdenin
  çevresi değişir.
- **GN17** — Derleyicinin ileti metni basılmaz; ölçülen şey yazımın kabul edilip
  edilmediğidir.

```java
// Olcek.java — ortak ölçüm çekirdeği: sınıf dosyasını derleyip okur
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Olcek {
    static ClassModel derle(String kaynak, String sinif) throws Exception {
        Path d = Files.createTempDirectory("olcek");
        Path k = d.resolve(sinif + ".java");
        Files.writeString(k, kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        if (ToolProvider.findFirst("javac").orElseThrow()
                .run(bos, bos, "-d", d.toString(), k.toString()) != 0)
            throw new IllegalStateException("derlenmedi: " + sinif);
        return ClassFile.of().parse(d.resolve(sinif + ".class"));
    }

    static List<String> komutlar(String kaynak, String sinif, String yontem) throws Exception {
        List<String> adlar = new ArrayList<>();
        for (MethodModel m : derle(kaynak, sinif).methods())
            if (m.methodName().stringValue().equals(yontem))
                for (CodeElement e : m.code().orElseThrow())
                    if (e instanceof Instruction i)
                        adlar.add(i.opcode().name().toLowerCase(Locale.ROOT));
        return adlar;
    }

    static String kisa(String ic) { return ic.substring(ic.lastIndexOf('/') + 1); }
}
```

```java
// Zorunlu.java — üç istisna türü, üç yazım biçimi
public class Zorunlu {
    static final String TIPLER = """
        class UstIstisna extends Exception { UstIstisna(String m) { super(m); } }
        class SessizIstisna extends RuntimeException { SessizIstisna(String m) { super(m); } }
        class AgirHata extends Error { AgirHata(String m) { super(m); } }
        """;

    static final String[] TURLER = {"UstIstisna", "SessizIstisna", "AgirHata"};

    static final String BILDIRMEDEN = "class C { static void f() { throw new %1$s(\"olay\"); } }";
    static final String BILDIREREK =
            "class C { static void f() throws %1$s { throw new %1$s(\"olay\"); } }";
    static final String YAKALAYARAK =
            "class C { static void f() { try { throw new %1$s(\"olay\"); } catch (%1$s e) { } } }";

    static String dene(String kalip, String tur) {
        try {
            Olcek.derle(TIPLER + kalip.formatted(tur), "C");
            return "derlendi";
        } catch (Exception e) {
            return "derlenmedi";
        }
    }

    public static void main(String[] args) {
        System.out.printf("%-16s %-12s %-12s %s%n",
                "istisna turu", "bildirmeden", "bildirerek", "yakalayarak");
        int gecen = 0;
        for (String t : TURLER) {
            String a = dene(BILDIRMEDEN, t), b = dene(BILDIREREK, t), c = dene(YAKALAYARAK, t);
            for (String s : new String[] {a, b, c}) if (s.equals("derlendi")) gecen++;
            System.out.printf("%-16s %-12s %-12s %s%n", t, a, b, c);
        }
        System.out.println("dokuz yazimin derlenen sayisi: " + gecen);
    }
}
```

```
istisna turu     bildirmeden  bildirerek   yakalayarak
UstIstisna       derlenmedi   derlendi     derlendi
SessizIstisna    derlendi     derlendi     derlendi
AgirHata         derlendi     derlendi     derlendi
dokuz yazimin derlenen sayisi: 8
```

Dokuz yazımın **sekizi** derleniyor. Reddedilen tek yazım tablonun sol üst köşesindedir:
denetlenen bir istisnayı fırlatıp ne bildiren ne yakalayan yöntem. Denetlenen istisnanın
derleme zamanında zorladığı şeyin tamamı budur — **bir seçim yapmak**. Bildirmek ile
yakalamak arasında derleyici için hiçbir fark yoktur; ikisi de aynı kapıyı kapatır ve iki
sütun da eşit derecede geçerlidir.

İkinci ve üçüncü satırlar ayrımın karşı yüzüdür. Denetlenmeyen istisna ile ağır hata için üç
yazımın **üçü** de derleniyor: bildirilebilirler, yakalanabilirler, ama hiçbiri zorunlu
değildir. Denetlenmeyen bir istisna kaynağın hiçbir yerinde iz bırakmadan bir yöntemden
çıkabilir.

Buradan iki cümle çıkıyor. Birincisi: denetlenen/denetlenmeyen ayrımı bir davranış ayrımı
değildir; ikisi de fırlatılır, ikisi de yakalanır, ikisi de çağrı yığıtını yukarı doğru
katlar. Ayrım yalnız **derleyicinin bir yazımı kabul edip etmemesinde** görünür. İkincisi:
bu zorunluluk bildirilen tipe bakar. Derleyici gövdenin gerçekte ne fırlatacağını
çalıştırarak öğrenmez; çağrılan yöntemlerin **bildirimlerini** okur.

## Bildirim Bir Özniteliktir, Komut Değil

- **GN18** — İki kaynak da tek bir yöntem taşır ve gövdeleri aynı biçimdedir; yalnız
  fırlatılan istisnanın türü değişir.

```java
// Bildirim.java — denetlenen istisna bildirimi sınıf dosyasının neresinde
import java.lang.classfile.*;
import java.lang.classfile.attribute.ExceptionsAttribute;
import java.util.List;

public class Bildirim {
    static final String TIPLER = """
        class UstIstisna extends Exception { UstIstisna(String m) { super(m); } }
        class SessizIstisna extends RuntimeException { SessizIstisna(String m) { super(m); } }
        """;

    static final String BILDIREN = TIPLER
            + "class C { static void f() throws UstIstisna { throw new UstIstisna(\"olay\"); } }";
    static final String BILDIRMEYEN = TIPLER
            + "class C { static void f() { throw new SessizIstisna(\"olay\"); } }";

    static void yaz(String ad, String kaynak) throws Exception {
        for (MethodModel m : Olcek.derle(kaynak, "C").methods())
            if (m.methodName().stringValue().equals("f"))
                System.out.printf("%-14s tanim %-6s bildirim ozniteligi %s%n", ad,
                        m.methodType().stringValue(),
                        m.findAttribute(Attributes.exceptions())
                                .map(ExceptionsAttribute::exceptions).orElse(List.of())
                                .stream().map(e -> Olcek.kisa(e.asInternalName())).toList());
    }

    public static void main(String[] args) throws Exception {
        yaz("denetlenen", BILDIREN);
        yaz("denetlenmeyen", BILDIRMEYEN);
        System.out.println();
        System.out.println("denetlenen firlatma   : " + Olcek.komutlar(BILDIREN, "C", "f"));
        System.out.println("denetlenmeyen firlatma: " + Olcek.komutlar(BILDIRMEYEN, "C", "f"));
    }
}
```

```
denetlenen     tanim ()V    bildirim ozniteligi [UstIstisna]
denetlenmeyen  tanim ()V    bildirim ozniteligi []

denetlenen firlatma   : [new, dup, ldc, invokespecial, athrow]
denetlenmeyen firlatma: [new, dup, ldc, invokespecial, athrow]
```

İki yöntemin tanımı **aynıdır**: `()V`. Yöntemin sanal makineye görünen kimliği parametre
tipleriyle dönüş tipinden oluşur; fırlatabileceği istisna oraya girmez. Bildirim ayrı bir
**öznitelikte** durur ve denetlenmeyen istisnada o öznitelik boştur.

Alt satırlar bunun kod tarafındaki karşılığıdır: iki fırlatma **birebir aynı beş komuttur**.
Nesneyi üret, çoğalt, iletiyi yükle, yapıcıyı çağır, fırlat. Sınıf dosyasının yürütülen
bölümünde denetlenen ile denetlenmeyen istisna arasında hiçbir fark yoktur. Fark yalnız
derleyicinin okuduğu öznitelikte durur — tıpkı tip parametresinin yalnız imza özniteliğinde
durması gibi. Bu kursun ilk dersinde ölçülen kalıp burada bir kez daha çıkıyor: **bilgi sınıf
dosyasında veri olarak yazılıdır, denetim olarak değil.**

## Sanal Makine Bildirime Bakmaz

- **GN19** — Ölçüm hiçbir dış araç kullanmadan, dilin kendi içinde kurulur; istisna nesnesi
  ile ilgili tek bilgi olarak sınıf adı basılır.

```java
// Gizli.java — sanal makine bildirimi denetliyor mu
class UstIstisna extends Exception {
    UstIstisna(String m) { super(m); }
}

public class Gizli {
    @SuppressWarnings("unchecked")
    static <T extends Throwable> void firlat(Throwable t) throws T {
        throw (T) t;
    }

    static void bildirmeyen() {
        Gizli.<RuntimeException>firlat(new UstIstisna("olay"));
    }

    public static void main(String[] args) {
        try {
            bildirmeyen();
            System.out.println("istisna cikmadi");
        } catch (Throwable t) {
            System.out.println("bildirmeyen yontemden cikan   : " + t.getClass().getSimpleName());
            System.out.println("denetlenen istisna mi         : "
                    + (t instanceof RuntimeException || t instanceof Error ? "hayir" : "evet"));
            System.out.println("sanal makine yolu kesti mi    : hayir");
        }
    }
}
```

```
bildirmeyen yontemden cikan   : UstIstisna
denetlenen istisna mi         : evet
sanal makine yolu kesti mi    : hayir
```

`bildirmeyen` yöntemi hiçbir istisna bildirmiyor ve bildirim özniteliği boş. Buna karşın o
yöntemden **denetlenen** bir istisna çıkıyor ve çalışma boyunca hiçbir şey bu yolu kesmiyor.

Bunun nasıl olduğu bu konunun iki yarısını birleştiriyor. `firlat` yöntemi `T` tip
parametresiyle bildirilmiş, çağrı yerinde `T` yerine `RuntimeException` konmuş, yani
derleyici için bu yöntem denetlenmeyen bir istisna fırlatır. Gövdedeki dönüşüm ise `T`'ye
yapılıyor ve **tip silme yüzünden o dönüşüm sınıf dosyasında hiçbir denetim bırakmıyor**.
Derleyicinin bildirimle kurduğu güvence, tip parametresinin silinmesiyle açılan boşluktan
geçip gidiyor.

Ölçümün söylediği kural şudur: denetlenen istisna bildirimi **derleyiciler arası bir
sözleşmedir**, çalışma zamanı güvencesi değildir. Sanal makine bir yöntemden çıkan istisnanın
o yöntemin bildiriminde yazıp yazmadığını sınamaz. Bu, kursun ölçüsüne göre saf bir
"bildirilen tip" kararıdır ve çalışma zamanı tipinin bu konuda söyleyecek hiçbir şeyi yoktur.

## Bildirim Daraltılabilir, Genişletilemez

- **GN20** — Beş denemede yalnız alt sınıfın bildirimi değişir; üst sınıfın bildirimi,
  istisna tipleri ve çağıran kod her denemede aynıdır.

```java
// Devir.java — yeniden tanımlayan yöntem bildirimi genişletebilir mi
public class Devir {
    static final String KALIP = """
        class UstIstisna extends Exception { UstIstisna(String m) { super(m); } }
        class AltIstisna extends UstIstisna { AltIstisna(String m) { super(m); } }
        class BagimsizIstisna extends Exception { BagimsizIstisna(String m) { super(m); } }
        class Ust { void f() throws UstIstisna { } }
        class Alt extends Ust { @Override void f() %s { } }
        class C { static void c(Ust u) { try { u.f(); } catch (Exception e) { } } }
        """;

    static void dene(String bildirim) {
        String ad = bildirim.isEmpty() ? "(bildirimsiz)" : bildirim;
        try {
            Olcek.derle(KALIP.formatted(bildirim), "C");
            System.out.printf("Alt.f() %-28s -> derlendi%n", ad);
        } catch (Exception e) {
            System.out.printf("Alt.f() %-28s -> derlenmedi%n", ad);
        }
    }

    public static void main(String[] args) throws Exception {
        for (String b : new String[] {"throws UstIstisna", "throws AltIstisna", "",
                "throws BagimsizIstisna", "throws RuntimeException"})
            dene(b);

        System.out.println();
        String cagri = """
            class UstIstisna extends Exception { UstIstisna(String m) { super(m); } }
            class Ust { void f() throws UstIstisna { } }
            class Alt extends Ust { @Override void f() { } }
            class C { static void c() { %s.f(); } }
            """;
        for (String bildirilen : new String[] {"new Ust()", "new Alt()"}) {
            String etiket = bildirilen.equals("new Ust()") ? "Ust" : "Alt";
            try {
                Olcek.derle(cagri.formatted(bildirilen), "C");
                System.out.printf("bildirilen tip %-4s cagrisi islemeden -> derlendi%n", etiket);
            } catch (Exception e) {
                System.out.printf("bildirilen tip %-4s cagrisi islemeden -> derlenmedi%n", etiket);
            }
        }
    }
}
```

```
Alt.f() throws UstIstisna            -> derlendi
Alt.f() throws AltIstisna            -> derlendi
Alt.f() (bildirimsiz)                -> derlendi
Alt.f() throws BagimsizIstisna       -> derlenmedi
Alt.f() throws RuntimeException      -> derlendi

bildirilen tip Ust  cagrisi islemeden -> derlenmedi
bildirilen tip Alt  cagrisi islemeden -> derlendi
```

Üst tablonun kuralı tek yönlüdür. Yeniden tanımlayan yöntem üst sınıfın bildirdiği istisnayı
aynen bildirebilir, onun bir alt tipini bildirebilir, hiçbir şey bildirmeyebilir — ama
**başka bir denetlenen istisna ekleyemez**. Denetlenmeyen istisna bildirmek her durumda
serbesttir, çünkü onun için zaten hiçbir zorunluluk yoktu.

Alt iki satır bu kuralın gerekçesini ölçüyor ve kursun sorusunu tam ortasından yanıtlıyor.
`Alt` sınıfının yöntemi hiçbir istisna bildirmiyor. Aynı nesne `Alt` olarak adlandırıldığında
çağrı işlemeden **derleniyor**; `Ust` olarak adlandırıldığında **derlenmiyor**. Nesne aynı,
çalıştırılacak gövde aynı, sonuç aynı — değişen tek şey adın bildirilen tipi. Derleyici
işleme zorunluluğunu yalnız oradan okuduğu için, alt sınıfın bildirimi genişletmesine izin
verilseydi `Ust` üzerinden yazılmış hiçbir çağrı güvenilir olmazdı.

## Yakalamanın İki Tarafı

```java
// Yakala.java — yakalamanın iki tarafı: hangi dal yazılabilir, hangi dal çalışır
class UstIstisna extends Exception {
    UstIstisna(String m) { super(m); }
}

class AltIstisna extends UstIstisna {
    AltIstisna(String m) { super(m); }
}

public class Yakala {
    static final String KALIP = """
        class UstIstisna extends Exception { UstIstisna(String m) { super(m); } }
        class AltIstisna extends UstIstisna { AltIstisna(String m) { super(m); } }
        class BagimsizIstisna extends Exception { BagimsizIstisna(String m) { super(m); } }
        class C {
            static void firlat() throws UstIstisna { throw new AltIstisna("olay"); }
            static void y() throws UstIstisna { try { firlat(); } catch (%s e) { } }
        }
        """;

    static void dene(String dal) {
        try {
            Olcek.derle(KALIP.formatted(dal), "C");
            System.out.printf("catch (%-19s -> derlendi%n", dal + " e)");
        } catch (Exception e) {
            System.out.printf("catch (%-19s -> derlenmedi%n", dal + " e)");
        }
    }

    static void firlat() throws UstIstisna {
        throw new AltIstisna("olay");
    }

    public static void main(String[] args) {
        for (String dal : new String[]
                {"UstIstisna", "AltIstisna", "BagimsizIstisna", "RuntimeException", "Exception"})
            dene(dal);

        System.out.println();
        try {
            firlat();
        } catch (UstIstisna e) {
            System.out.println("UstIstisna dali yakaladi, nesnenin sinifi: "
                    + e.getClass().getSimpleName());
        }
        try {
            firlat();
        } catch (AltIstisna e) {
            System.out.println("iki dal varken calisan dal               : AltIstisna");
        } catch (UstIstisna e) {
            System.out.println("iki dal varken calisan dal               : UstIstisna");
        }
    }
}
```

```
catch (UstIstisna e)       -> derlendi
catch (AltIstisna e)       -> derlendi
catch (BagimsizIstisna e)  -> derlenmedi
catch (RuntimeException e) -> derlendi
catch (Exception e)        -> derlendi

UstIstisna dali yakaladi, nesnenin sinifi: AltIstisna
iki dal varken calisan dal               : AltIstisna
```

Üst tablo yakalamanın **derleme zamanı** yüzüdür ve reddedilen tek dal ilginç olanıdır.
`BagimsizIstisna` denetlenen bir istisnadır ve `try` bloğunun içindeki çağrının bildiriminde
geçmiyor; derleyici o dalı **yazdırmıyor** bile. Buradaki karar tümüyle bildirilen tiplerden
gelir: derleyici bloğun içinden hangi denetlenen istisnaların çıkabileceğini bildirimlerden
hesaplar ve o kümenin dışında kalan bir dalı ölü kabul eder. `RuntimeException` dalının
geçmesi aynı kuralın diğer yüzüdür — denetlenmeyen istisna her yerden çıkabileceği için hiçbir
zaman ölü sayılmaz.

Alt satırlar aynı yakalamanın **çalışma zamanı** yüzüdür ve bu dersin sınırlayıcı ölçümüdür.
Fırlatan yöntem `UstIstisna` bildiriyor, ama fırlattığı nesnenin sınıfı `AltIstisna`.
`UstIstisna` dalı bu nesneyi yakalıyor ve yakalanan nesnenin sınıfı `AltIstisna` olarak
okunuyor. İki dal yan yana konduğunda çalışan dal `AltIstisna` — yani seçim bildirilen tipten
değil, **çalışma zamanı tipinden** yapılıyor.

Aynı yapıda iki karşıt yanıt bulunması bu kursun temel iddiasının bu konudaki biçimidir. Bir
`catch` dalının **yazılabilir olması** bildirilen tipe, **çalışması** çalışma zamanı tipine
bağlıdır. Yanlış tarafa bakan bir programcı bunu iki ayrı biçimde ödeyebilir: dar bir dalı
üst tipe göre yazıp hiç çalışmadığını görmek, ya da geniş bir dalın altına yazdığı dar dalın
derlenmediğini görmek.

## Özet

- Denetlenen istisnanın derleme zamanında zorladığı tek şey bir seçimdir: bildirmek ya da
  yakalamak. Dokuz yazımdan yalnız biri reddedilir; denetlenmeyen istisna ile ağır hatanın üç
  yazımı da geçerlidir.
- Denetlenen ile denetlenmeyen istisna arasında davranış farkı yoktur; fark yalnız
  derleyicinin bir yazımı kabul edip etmemesindedir.
- Bildirim yöntemin tanımına girmez, ayrı bir öznitelikte durur; iki türün fırlatma kodu
  birebir aynı beş komuttur.
- Sanal makine bildirimi denetlemez: tip parametresi üzerinden fırlatılan denetlenen bir
  istisna, hiçbir şey bildirmeyen bir yöntemden çıkabilir. Bildirim bir derleme zamanı
  sözleşmesidir.
- Yeniden tanımlayan yöntem bildirimi daraltabilir, genişletemez; aynı nesneye `Ust` diye
  bakan çağrı işlemek zorundadır, `Alt` diye bakan çağrı değildir.
- Sınırlayıcı ölçüm: yakalamanın hangi dalının yazılabileceğine bildirilen tip, hangi dalın
  çalışacağına çalışma zamanı tipi karar verir. Aynı yapıda iki karşıt yanıt bir arada durur.

## Sonraki Adım

Bu derste bir istisna fırlatıldı, bildirildi ve yakalandı; her ölçümde tek bir olay vardı ve
onun nereye gittiği izlendi. Oysa gerçek bir gövdede istisna genellikle yalnız değildir: bir
kaynak açılmışsa, gövde düşerken o kaynağın kapatılması gerekir ve kapatma da düşebilir. Bu
durumda iki istisna aynı anda ortaya çıkar ve biri diğerinin üstünü örtebilir. Sıradaki ders
elle yazılan kapatma ile derleyicinin ürettiği kapatmayı yan yana koyup kaç istisnanın
kaybolduğunu, kaçının korunduğunu ve kapatmanın hangi sırayla yapıldığını sayar.
