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ü.
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:
- İstisna fırlatılınca try bloğunun geri kalanı atlanır.
- catch’ler yukarıdan aşağı denenir, ilk uyan kazanır.
- finally her durumda çalışır, return olsa bile.
- Yerel handler yoksa istisna çağırana yayılır.
- 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ı
| Soru | Java’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ı
| Java | C++ | Ada | |
|---|---|---|---|
| Ne fırlatılır? | Throwable alt sınıfı nesneler | Herhangi bir tip (int, string, nesne) | exception olarak bildirilen isimler |
| Handler | catch (Tip e) | catch (Tip e), catch (...) hepsini yakalar | Bloğun sonunda exception when E1 => ... |
| Temizlik | finally, try-with-resources | finally yok; destructor’lar (RAII) | |
| Bildirim | throws (checked için zorunlu) | noexcept (eski throw() listesi) | |
| Built-in | Çok sayıda | Az (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ışır | Atlanır |
| Handler bitince istisna noktasına dönülür | Termination: try-catch’ten sonraya |
| finally sadece istisnada çalışır | Her durumda |
| finally’deki atama dönüş değerini değiştirir | Saklanan 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 gerekir | Sadece checked istisnalar |
| C++‘ta finally var | Yok; destructor’lar |
| setTimeout(…, 0) hemen çalışır | Senkron kod ve mikro görevlerden sonra |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
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
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.
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
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).
What does this method return?
static int h() {
int x = 10;
try { return x; }
finally { x = 20; }
}Cevabı göster
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ü.
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): 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).
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
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.
Does this compile? What are the two ways to fix it?
static void read() { throw new java.io.IOException("disk"); }Cevabı göster
throws IOException in the headerIOException 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.
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
"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.
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
İç 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.
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
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.
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
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.
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
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.
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
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.