SEN2001 · 12. hafta · Exception Handling and Event Handling

Exception handling ve event handling

İstisna kavramı ve tasarım soruları, handler'a bağlama ve propagation, Java'da try/catch/finally akışı, checked/unchecked istisnalar, C++ ve Ada karşılaştırması; olay güdümlü programlama, listener modeli ve JavaScript olay döngüsü.

exceptionexception handlerraisethrowcatchpropagationcontinuationresumptionterminationfinally clausechecked exceptionunchecked exceptionthrows clausebuilt-in exceptionuser-defined exceptioneventevent handlerlistenerevent-driven programmingevent loopcallback

Bu hafta iki “olağan akış dışı” yapı: exception (bir şey ters gidince ne olur?) ve event (bir şey olunca ne çalışır?). İkisinde de ortak fikir: kodu yazdığın sırayla değil, bir olayın tetiklemesiyle çalışan bir handler var. Sınavda en çok try/catch/finally akış izleme ve dil tasarım karşılaştırmaları çıkar.

Altın kurallar:

  1. İstisna fırlatılınca try bloğunun geri kalanı atlanır.
  2. catch’ler yukarıdan aşağı denenir, ilk uyan kazanır.
  3. finally her durumda çalışır, return olsa bile.
  4. Yerel handler yoksa istisna çağırana yayılır.
  5. Java’da checked exception ya yakalanır ya bildirilir.

İstisna nedir?

Exception, donanımın ya da yazılımın tespit ettiği ve özel işlem gerektiren olağandışı bir olaydır: sıfıra bölme, dizi sınırı aşımı, dosya bulunamaması. İstisnayı işleyen koda exception handler denir. İstisnanın oluşması raise (Java’da throw) olarak adlandırılır.

İstisna desteği olmayan bir dilde hata nasıl bildirilir? Fonksiyon özel bir değer (hata kodu) döndürür ya da bir parametreye durum yazar. Sorun: her çağrıdan sonra kontrol etmek gerekir, kod okunmaz hale gelir ve kontrol etmeyi unutmak kolaydır. Dil desteği bunu ayırır: normal kod tek yerde, hata işleme başka yerde.

Tasarım soruları

SoruJava’nın cevabı
Handler’lar nasıl belirtilir, kapsamları ne?try bloğuna bağlı catch cümleleri
İstisna bir handler’a nasıl bağlanır?İstisnanın sınıfı catch parametresinin sınıfıyla (ya da alt sınıfıyla) eşleşir
Yerel handler yoksa?Çağırana yayılır (propagation); hiç yoksa thread sonlanır
Handler bitince nereye dönülür?try-catch’ten sonraki ifadeye (termination)
Sonlandırma kodu (cleanup) var mı?finally
Built-in istisnalar var mı?Evet: ArithmeticException, NullPointerException …
Kullanıcı istisna tanımlayabilir mi?Evet: Exception’dan türeyen sınıf

Continuation: handler bittikten sonra kontrol nereye gider? Resumption modeli istisnanın oluştuğu yere geri döner. Termination modeli dönmez, handler’ı içeren yapının sonrasından devam eder. Java, C++, Ada, C#: termination.

Java’da akış

try {
    System.out.print("1 ");
    int[] x = new int[2];
    x[5] = 1;                    // istisna: geri kalan atlanır
    System.out.print("2 ");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.print("3 ");
} finally {
    System.out.print("4 ");
}
System.out.println("5");
1 3 4 5

finally ve return

static int f() { try { return 1; } finally { System.out.print("fin "); } }
static int g() { try { return 1; } finally { return 2; } }
static int h() { int x = 10; try { return x; } finally { x = 20; } }
fin 1
2
10

finally, try’daki return’den sonra ama metot dönmeden önce çalışır. g’de finally’nin return’ü ezer. h’de dönüş değeri return x anında saklandı; sonradan x’i değiştirmek onu etkilemez.

Propagation

static void c(int n) { System.out.print("c" + n + " ");
    if (n == 0) throw new IllegalStateException("zero"); }
static void b(int n) {
    try { c(n); System.out.print("b-after "); }
    catch (IllegalArgumentException e) { System.out.print("b-catch "); }
    finally { System.out.print("b-fin "); }
}
static void a(int n) {
    try { b(n); }
    catch (RuntimeException e) { System.out.print("a-catch:" + e.getMessage() + " "); }
}
// a(1); a(0);
c1 b-after b-fin 
c0 b-fin a-catch:zero 

İzleme tarifi: istisna nerede doğdu? O fonksiyonda uyan catch var mı? Yoksa finally’leri çalıştırarak bir üst çağırana çık, aynı soruyu sor.

Sınıf hiyerarşisi: checked ve unchecked

Throwable
├── Error                  (JVM hataları: OutOfMemoryError)      unchecked
└── Exception                                                     checked
    ├── IOException → FileNotFoundException                       checked
    └── RuntimeException                                          unchecked
        ├── ArithmeticException, NullPointerException
        ├── ArrayIndexOutOfBoundsException, ClassCastException
        └── IllegalArgumentException → NumberFormatException

Checked istisnalar için derleyici iki seçenekten birini zorunlu tutar: metot içinde yakala ya da başlıkta throws ile bildir. Unchecked olanlar (RuntimeException ve Error alt sınıfları) her yerde oluşabileceği için bildirim istenmez.

static void read() { throw new java.io.IOException("disk"); }
error: unreported exception IOException; must be caught or declared to be thrown

catch sırası özelden genele olmalı; yoksa alttaki catch’e hiç ulaşılamaz ve derleyici hata verir:

catch (IOException e) { } catch (FileNotFoundException e) { }
error: exception FileNotFoundException has already been caught

Kullanıcı tanımlı istisna

static class InsufficientFunds extends Exception { InsufficientFunds(String m) { super(m); } }
static void withdraw(int bal, int amt) throws InsufficientFunds {
    if (amt > bal) throw new InsufficientFunds("need " + (amt - bal));
}
try { withdraw(50, 80); } catch (InsufficientFunds e) { System.out.println("caught " + e.getMessage()); }
caught need 30

Handler içinden yeniden fırlatma

try {
    try { throw new RuntimeException("inner"); }
    catch (RuntimeException e) { System.out.print("handle "); throw new IllegalArgumentException("outer"); }
    finally { System.out.print("fin "); }
} catch (IllegalArgumentException e) { System.out.println("rethrown " + e.getMessage()); }
handle fin rethrown outer

Dil karşılaştırması

JavaC++Ada
Ne fırlatılır?Throwable alt sınıfı nesnelerHerhangi bir tip (int, string, nesne)exception olarak bildirilen isimler
Handlercatch (Tip e)catch (Tip e), catch (...) hepsini yakalarBloğun sonunda exception when E1 => ...
Temizlikfinally, try-with-resourcesfinally yok; destructor’lar (RAII)
Bildirimthrows (checked için zorunlu)noexcept (eski throw() listesi)
Built-inÇok sayıdaAz (standart kütüphane)Constraint_Error, Program_Error, Storage_Error, Tasking_Error

Ada’da bir istisna bir blokta işlenmezse, bloğu içeren birime; alt programdaysa çağırana yayılır. C++‘ta istisna fırlatılınca yığın geri sarılırken yerel nesnelerin destructor’ları çağrılır, kaynaklar böyle serbest bırakılır.

Event handling

Event (olay), bir şeyin olduğunun bildirimidir: düğmeye tıklama, tuşa basma, zamanlayıcının dolması, ağdan veri gelmesi. Event handler, olaya yanıt olarak çalışan koddur. İstisnadan farkı: olaylar olağan ve beklenen şeylerdir, programın asıl işidir. GUI’ler ve web sayfaları event-driven çalışır: program olay bekler, olay gelince ilgili handler’ı çağırır.

Listener modeli (Java)

Olay kaynağı, kendisini dinleyen nesnelerin listesini tutar. Olay olunca hepsinin handler metodunu çağırır. Swing’de button.addActionListener(listener) ve actionPerformed(ActionEvent e).

interface Listener { void on(String e); }
static class Button {
    List<Listener> ls = new ArrayList<>();
    void add(Listener l) { ls.add(l); }
    void click() { for (Listener l : ls) l.on("click"); }
}
Button btn = new Button(); StringBuilder log = new StringBuilder();
btn.add(e -> log.append("A:" + e + " "));
btn.add(e -> log.append("B "));
btn.click(); btn.click();
System.out.println(log.toString().trim());
A:click B A:click B

C#‘ta aynı fikir delegate ve event anahtar kelimesiyle dile gömülüdür: button.Click += handler;.

JavaScript olay döngüsü

Tarayıcı ve Node.js tek bir thread’de bir event loop çalıştırır. Önce senkron kod biter; sonra mikro görevler (promise geri çağrıları); sonra olay kuyruğundaki görevler (zamanlayıcılar, tıklamalar).

const out = [];
setTimeout(() => out.push("timeout"), 0);
Promise.resolve().then(() => out.push("promise"));
out.push("sync1");
queueMicrotask(() => out.push("micro"));
out.push("sync2");
setTimeout(() => console.log(out.join(" ")), 10);
sync1 sync2 promise micro timeout

Node’un EventEmitter’ı listener modelinin aynısı: handler’lar kayıt sırasıyla, senkron çalışır; once ile eklenen ilk olaydan sonra kendini siler.

first 1 | once 1 | third | first 2 | third

Sık yapılan hatalar

YanlışDoğrusu
İstisnadan sonra try’ın kalanı çalışırAtlanır
Handler bitince istisna noktasına dönülürTermination: try-catch’ten sonraya
finally sadece istisnada çalışırHer durumda
finally’deki atama dönüş değerini değiştirirSaklanan değer değişmez; finally’de return ise ezer
Genel catch önce yazılabilirÖzel olan önce; yoksa derleme hatası
Her istisna için throws gerekirSadece checked istisnalar
C++‘ta finally varYok; destructor’lar
setTimeout(…, 0) hemen çalışırSenkron kod ve mikro görevlerden sonra

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

What is printed in Java?

try {
    System.out.print("1 ");
    int[] x = new int[2];
    x[5] = 1;
    System.out.print("2 ");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.print("3 ");
} finally {
    System.out.print("4 ");
}
System.out.println("5");
Cevabı göster
1 3 4 5

x[5] istisna fırlatır; try bloğunun geri kalanı ("2") atlanır. Uyan catch çalışır: 3. finally her durumda çalışır: 4. İstisna yakalandığı için program try-catch'ten sonra devam eder: 5. Java termination modelini kullanır: istisna noktasına geri dönülmez.

Soru 2zor

What do these two methods return, and what does f print?

static int f() { try { return 1; } finally { System.out.print("fin "); } }
static int g() { try { return 1; } finally { return 2; } }
Cevabı göster
f prints fin and returns 1; g returns 2

finally, try'daki return'den sonra ama metot gerçekten dönmeden önce çalışır. f'de finally sadece yazdırır, dönüş değeri 1 kalır. g'de finally'deki return, try'ın return'ünü ezer: 2. finally içinde return yazmak bu yüzden kötü bir alışkanlık (istisnaları da yutar).

Soru 3zor

What does this method return?

static int h() {
    int x = 10;
    try { return x; }
    finally { x = 20; }
}
Cevabı göster
10

return x ifadesi değeri (10) o anda hesaplayıp saklar. finally x'i 20 yapar ama saklanmış dönüş değeri değişmez. int değerle saklandığı için değişiklik görünmez; bir nesne döndürülseydi ve finally nesnenin içini değiştirseydi değişiklik görünürdü.

Soru 4orta

What is printed by a(1) and by a(0)?

static void c(int n) { System.out.print("c" + n + " ");
    if (n == 0) throw new IllegalStateException("zero"); }
static void b(int n) {
    try { c(n); System.out.print("b-after "); }
    catch (IllegalArgumentException e) { System.out.print("b-catch "); }
    finally { System.out.print("b-fin "); }
}
static void a(int n) {
    try { b(n); }
    catch (RuntimeException e) { System.out.print("a-catch:" + e.getMessage() + " "); }
}
Cevabı göster
a(1): c1 b-after b-fin ; a(0): c0 b-fin a-catch:zero

a(1): istisna yok, normal akış, finally yine çalışır. a(0): c IllegalStateException fırlatır. c'de handler yok → b'ye yayılır (propagation). b'nin catch'i IllegalArgumentException bekliyor, uymaz; ama finally çalışır. İstisna a'ya yayılır, RuntimeException ile eşleşir (üst sınıf).

Soru 5orta

Does this Java code compile? Why?

try { throw new java.io.FileNotFoundException(); }
catch (java.io.IOException e) { }
catch (java.io.FileNotFoundException e) { }
Cevabı göster
No: FileNotFoundException has already been caught

Handler'lar yukarıdan aşağı denenir ve ilk uyan seçilir. IOException, FileNotFoundException'ın üst sınıfı; ikinci catch'e hiç ulaşılamaz (unreachable). Java bunu derleme hatası yapar. Doğru sıra: özelden genele.

Soru 6orta

Does this compile? What are the two ways to fix it?

static void read() { throw new java.io.IOException("disk"); }
Cevabı göster
No; catch it inside read, or declare throws IOException in the header

IOException bir checked exception: Java derleyicisi ya yakalanmasını ya da metodun throws listesinde bildirilmesini zorunlu tutar ("unreported exception IOException; must be caught or declared to be thrown"). Error ve RuntimeException'ın alt sınıfları (NullPointerException, ArithmeticException) unchecked'tir, bildirim gerekmez.

Soru 7orta

What is printed in Java?

int count = 0;
for (String s : new String[]{"12", "x", "7", ""}) {
    try { count += Integer.parseInt(s); }
    catch (NumberFormatException e) { count += 100; }
}
System.out.println(count);
Cevabı göster
219

"12" → 12. "x" → NumberFormatException → +100. "7" → +7. Boş string de sayı değil → +100. 12 + 100 + 7 + 100 = 219. try-catch döngünün içinde olduğu için istisna sadece o turu etkiler, döngü sürer.

Soru 8zor

What is printed in Java?

try {
    try { throw new RuntimeException("inner"); }
    catch (RuntimeException e) {
        System.out.print("handle ");
        throw new IllegalArgumentException("outer");
    }
    finally { System.out.print("fin "); }
} catch (IllegalArgumentException e) {
    System.out.println("rethrown " + e.getMessage());
}
Cevabı göster
handle fin rethrown outer

İç catch istisnayı yakalar, sonra yeni bir istisna fırlatır. Dış catch'e gitmeden önce iç try'ın finally'si çalışır. Handler'ın içinden fırlatılan istisna aynı try'ın diğer catch'lerine değil dış bloğa gider.

Soru 9orta

Name the language: (a) exceptions are objects of classes descended from Throwable, with checked and unchecked kinds; (b) any type can be thrown, catch (...) catches everything, no finally; (c) handlers are attached to blocks with exception when ... => and predefined exceptions include Constraint_Error.

Cevabı göster
(a) Java (b) C++ (c) Ada

Java: istisnalar nesne, Throwable → Error / Exception → RuntimeException. C++: int, string, herhangi bir sınıf fırlatılabilir; catch (...) genel handler; temizlik destructor'larla (RAII) yapılır. Ada: istisna bir blok ya da alt programın sonundaki handler'larda yakalanır; Constraint_Error dizi sınırı veya aralık ihlalinde yükselir.

Soru 10zor

In which order are the strings logged by this JavaScript code?

setTimeout(() => out.push("timeout"), 0);
Promise.resolve().then(() => out.push("promise"));
out.push("sync1");
queueMicrotask(() => out.push("micro"));
out.push("sync2");
Cevabı göster
sync1 sync2 promise micro timeout

Olay güdümlü çalışma: önce senkron kod sonuna kadar çalışır. Sonra mikro görev kuyruğu (promise, queueMicrotask) eklenme sırasıyla. En son olay kuyruğundaki zamanlayıcı geri çağrısı (setTimeout 0 bile olsa). Handler'lar olay döngüsü tarafından çağrılır, programın akışı içinde değil.

Soru 11kolay

A Java Button keeps a list of listeners and calls each with the event name on click(). Two listeners are registered: one appends "A:" + e + " ", the other appends "B ". What does the log contain after two clicks?

Cevabı göster
A:click B A:click B

Listener (observer) modeli: olay kaynağı (Button) kendisini dinleyen nesneleri bir listede tutar ve olay olunca her birinin handler metodunu kayıt sırasıyla çağırır. Java Swing'de addActionListener ve actionPerformed aynı yapıdır. Kaynak, dinleyicilerin ne yaptığını bilmez.

Soru 12orta

With Node's EventEmitter, on('save', first), once('save', once), on('save', third) are registered, then emit('save', 1) and emit('save', 2) are called. What is the sequence of handler calls?

Cevabı göster
first 1, once 1, third, first 2, third

Handler'lar kayıt sırasıyla çalışır. once ile eklenen handler ilk olaydan sonra kendini kaldırır, ikinci emit'te çağrılmaz. emit senkron çalışır: handler'lar emit dönmeden önce biter.

Doğrulama: Tüm Java çıktıları JDK ile çalıştırıldı (java verify/SEN2001/W12.java); catch sırası ve bildirilmemiş checked exception örnekleri javac ile derlenmeye çalışıldı ve beklenen derleme hatalarını verdi (verify/SEN2001/w12b/). JavaScript olay döngüsü sırası ve EventEmitter örneği node ile çalıştırıldı (verify/SEN2001/12.mjs). C++ ve Ada bilgileri kavramsal; derleyicileri kurulu değil.

Deftere not kartı

exception: olağandışı durum (donanım ya da yazılım), handler onu işleyen kod istisna desteği yoksa: hata kodu döndür / parametre ile bildir (her çağrıda kontrol, okunaksız) tasarım: handler'a bağlama, propagation, devam (termination vs resumption), finally, built-in? Java/C++/Ada: TERMINATION modeli (istisna noktasına dönülmez) propagation: yerel handler yoksa çağırana yayılır; hiç yoksa program biter catch'ler yukarıdan aşağı, İLK uyan. özelden genele yaz (yoksa derleme hatası) finally HER ZAMAN çalışır. finally'de return → try'ın return'ünü ezer return x; finally { x = 20; } → 10 döner (değer önceden saklandı) checked (Exception, IOException): yakala ya da throws. unchecked: RuntimeException, Error C++: her tip fırlatılabilir, catch(...), finally yok. Ada: exception when ... => event: kullanıcı/sistem eylemi bildirimi. handler olay olunca çağrılır listener: kaynak listeyi tutar, olayda kayıt sırasıyla çağırır (Swing addActionListener) JS: senkron → microtask (promise) → macrotask (setTimeout)