İstisna yönetimi: try, catch, finally, throw, throws
Hata olunca kontrol nereye gider, finally ne zaman çalışır, checked ve unchecked farkı, kendi istisna sınıfını yazmak. Çıktı izleme sorularının en sevileni.
Programın her şey yolundayken ne yaptığını yazmak kolay. İstisna yönetimi (exception handling), işler ters gittiğinde ne olacağını düzenler: kullanıcı sayı yerine harf girer, dosya yoktur, dizi sınırı aşılır. Sınavda bu konu neredeyse her zaman bir “What is printed?” izleme sorusu olarak gelir: hangi satırlar atlanır, hangi catch çalışır, finally nerede araya girer. Yanında checked istisnalarla ilgili derleme hataları ve “kendi istisna sınıfını yaz” sorusu.
Altın kurallar:
- İstisna fırladığı an try bloğunun kalanı atlanır. Kontrol eşleşen catch’e gider.
- Catch’ler yukarıdan aşağı denenir, ilk uyan çalışır. Uyan, istisnanın kendi tipi ya da üst sınıfıdır.
- finally her zaman çalışır: istisna olsa da olmasa da, return olsa da.
- Yakalanmayan istisna metodu anında bitirir ve çağırana geçer (stack unwinding).
- Checked istisna ya yakalanır ya
throwsile bildirilir. Unchecked (RuntimeException) için zorunluluk yok.
try, catch, finally
try {
int[] arr = {1, 2, 3};
System.out.println(arr[1]);
System.out.println(arr[3]); // istisna burada
System.out.println("not printed"); // atlanır
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught: " + e.getMessage());
} finally {
System.out.println("finally");
}
System.out.println("after");
2
Caught: Index 3 out of bounds for length 3
finally
after
Kurallar:
- try tek başına duramaz: en az bir catch ya da bir finally gerekir. Yoksa
'try' without 'catch', 'finally' or resource declarations. - Bir try’ın birden fazla catch’i olabilir; sadece biri çalışır.
- finally isteğe bağlıdır; varsa en sonda bir kez çalışır. Kaynakları kapatmak (dosya, bağlantı) için kullanılır.
Birden fazla catch ve sıra
static void trace(int k) {
try {
System.out.print("A");
if (k == 1) throw new IllegalStateException();
if (k == 2) throw new IllegalArgumentException();
System.out.print("B");
} catch (IllegalStateException e) {
System.out.print("C");
} catch (RuntimeException e) { // üst sınıf: her RuntimeException'ı yakalar
System.out.print("D");
} finally {
System.out.print("E");
}
System.out.println("F");
}
trace(0);
trace(1);
trace(2);
ABEF
ACEF
ADEF
Genel catch’i (Exception, RuntimeException) özel olanlardan önce yazarsan alttaki catch’e hiçbir zaman sıra gelmez ve derleyici bunu hata sayar: exception NumberFormatException has already been caught.
Multi-catch: aynı işi yapan catch’leri birleştirmek için catch (NumberFormatException | ArithmeticException e) yazılır. Listede birbirinin alt sınıfı olan tipler olamaz.
static void parse(String s) {
try {
int n = Integer.parseInt(s);
System.out.println(100 / n);
} catch (NumberFormatException | ArithmeticException e) {
System.out.println("bad input: " + e.getClass().getSimpleName());
}
}
parse("4"); parse("x"); parse("0");
25
bad input: NumberFormatException
bad input: ArithmeticException
finally ve return
static int f() {
try { return 1; }
finally { System.out.println("finally in f"); }
}
static int h() {
int x = 10;
try { return x; } // dönüş değeri 10 olarak saklandı
finally { x = 20; } // saklanan değeri etkilemez
}
System.out.println(f());
System.out.println(h());
finally in f
1
10
TUZAK · finally içinde return. finally’ye
return 2;yazarsan try’dan fırlayan istisna bile yutulur ve metot 2 döner (denemedethrow new RuntimeExceptionolan try +return 2olan finally, sessizce 2 döndürdü). Derleyici bunu reddetmez (sadece-Xlintaçıkken uyarı verir); asla böyle yazma ama sınavda görürsen cevap finally’deki değer.
İstisna hiyerarşisi: checked ve unchecked
Throwable
├─ Error (JVM sorunları: OutOfMemoryError, StackOverflowError; yakalanmaz)
└─ Exception (CHECKED: derleyici kontrol eder)
├─ IOException
│ └─ FileNotFoundException
├─ (senin extends Exception sınıfların)
└─ RuntimeException (UNCHECKED: derleyici karışmaz)
├─ ArithmeticException
├─ NullPointerException
├─ ClassCastException
├─ IndexOutOfBoundsException
│ ├─ ArrayIndexOutOfBoundsException
│ └─ StringIndexOutOfBoundsException
└─ IllegalArgumentException
└─ NumberFormatException
| Checked | Unchecked | |
|---|---|---|
| Kimden türer? | Exception (RuntimeException hariç) | RuntimeException |
| Derleyici ne ister? | Ya catch ya throws | Hiçbir şey |
| Ne zaman olur? | Programın kontrolü dışındaki durumlar: dosya yok, ağ koptu | Programlama hataları: null, sıfıra bölme, yanlış indeks |
| Örnek | IOException, FileNotFoundException | ArithmeticException, NullPointerException |
Sık görülen unchecked istisnalar ve onları üreten tek satırlar:
String s = null; s.length(); // NullPointerException
Object o = "hi"; Integer i = (Integer) o; // ClassCastException
System.out.println(10 % 0); // ArithmeticException: / by zero
System.out.println(10.0 / 0); // istisna YOK: Infinity
NullPointerException
ClassCastException
ArithmeticException: / by zero
Infinity
(İlk üç satırın çıktısı, her biri try/catch içinde çalıştırılıp yakalanan istisnanın adı basılarak alındı.)
throw ve throws
throwbir istisna fırlatır. Ardından bir nesne gelir:throw new IllegalArgumentException("amount must be positive");throwsmetodun imzasında, bu metodun hangi checked istisnaları dışarı bırakabileceğini bildirir:void withdraw(double a) throws InsufficientFundsException.
Checked bir istisnayı fırlatan ya da fırlatabilen bir metodu çağırıyorsan iki seçeneğin var: try/catch ile yakala ya da kendi imzana throws ekleyip sorumluluğu yukarı devret. İkisini de yapmazsan: unreported exception X; must be caught or declared to be thrown.
Bir de ters yönde kural var: try bloğunda hiçbir şey bir checked istisnayı fırlatamıyorsa onu yakalayamazsın: exception IOException is never thrown in body of corresponding try statement.
Stack unwinding
Bir metotta istisna olur ve orada yakalanmazsa metot o anda biter. İstisna, metodu çağıran yere “fırlatılmış” gibi davranır. Yakalayan bir catch bulunana kadar çağrı yığını (call stack) böyle geri sarılır. Yol üstündeki her finally çalışır.
static void level3() {
System.out.println("level3 start");
int[] a = new int[2];
a[5] = 1; // istisna
System.out.println("level3 end");
}
static void level2() {
System.out.println("level2 start");
try { level3(); }
finally { System.out.println("level2 finally"); }
System.out.println("level2 end");
}
static void level1() {
System.out.println("level1 start");
try { level2(); }
catch (ArrayIndexOutOfBoundsException e) {
System.out.println("level1 caught: " + e.getMessage());
}
System.out.println("level1 end");
}
level1();
level1 start
level2 start
level3 start
level2 finally
level1 caught: Index 5 out of bounds for length 2
level1 end
main → level1 → level2 → level3 (çağrı yığını)
level3: a[5] → istisna, catch yok → level3 biter ("end" yok)
level2: catch yok, finally VAR → "level2 finally" → istisna devam ("end" yok)
level1: catch uyuyor → "level1 caught..." → normal akış → "level1 end"
İstisna main’e kadar çıkar ve orada da yakalanmazsa program biter ve JVM yığın izini (stack trace) basar. level3() doğrudan main’den çağrılınca:
level3 start
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 2
at W09.level3(W09.java:31)
at W09.main(W09.java:221)
Stack trace aşağıdan yukarı okunur: main, level3’ü çağırdı; istisna level3’ün 31. satırında oldu.
Kendi istisna sınıfın
İş kuralı ihlallerini (yetersiz bakiye, geçersiz not) anlamlı bir tiple bildirmek için kendi istisna sınıfını yazarsın. Checked olsun istiyorsan Exception’dan, unchecked olsun istiyorsan RuntimeException’dan türet. Mesajı super(message) ile ilet.
class InsufficientFundsException extends Exception {
private final double shortfall;
public InsufficientFundsException(String message, double shortfall) {
super(message);
this.shortfall = shortfall;
}
public double getShortfall() { return shortfall; }
}
class Account {
private double balance;
Account(double b) { balance = b; }
void withdraw(double amount) throws InsufficientFundsException {
if (amount <= 0) throw new IllegalArgumentException("amount must be positive");
if (amount > balance)
throw new InsufficientFundsException("Not enough money", amount - balance);
balance -= amount;
}
double getBalance() { return balance; }
}
Account acc = new Account(100);
try {
acc.withdraw(30);
acc.withdraw(100);
System.out.println("not printed");
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage() + ", short by " + e.getShortfall());
}
System.out.println(acc.getBalance());
try {
acc.withdraw(-5);
} catch (InsufficientFundsException e) {
System.out.println("checked");
} catch (IllegalArgumentException e) {
System.out.println("unchecked: " + e.getMessage());
}
Not enough money, short by 30.0
70.0
unchecked: amount must be positive
İstisna nesnesinin metotları
| Metot | Ne verir? | Örnek |
|---|---|---|
getMessage() | Sadece mesaj | bad value |
toString() | Sınıf adı + mesaj | java.lang.IllegalArgumentException: bad value |
printStackTrace() | Yığın izini stderr’e basar | yukarıdaki stack trace |
getCause() | Bu istisnaya yol açan istisna | zincirleme |
Bir istisnayı yakalayıp daha anlamlı bir başkasına sarmaya exception chaining denir: throw new IOException("load failed", e);. Sonra e.getCause().getMessage() asıl sebebi (data.txt) verir.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| İstisnadan sonra try’ın kalanı da çalışır | Atlanır, catch’e geçilir |
| Birden fazla catch çalışabilir | Sadece ilk uyan |
catch (Exception e) en üste | Alttakiler erişilemez olur, derleme hatası |
| return varsa finally çalışmaz | Çalışır, return’den önce |
| finally’de değişkeni değiştirmek dönüşü değiştirir | Dönüş değeri önceden saklandı |
| NumberFormatException checked | RuntimeException ailesinden, unchecked |
throws istisnayı fırlatır | throw fırlatır, throws bildirir |
| Checked istisna yakalanmadan çağrılabilir | catch ya da throws şart |
10.0 / 0 ArithmeticException | double’da Infinity; sadece int bölmede istisna |
| Yakalanan istisnadan sonra program biter | Catch’ten sonra normal akış devam eder |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed?
try {
int[] arr = {1, 2, 3};
System.out.println(arr[1]);
System.out.println(arr[3]);
System.out.println("not printed");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught: " + e.getMessage());
} finally {
System.out.println("finally");
}
System.out.println("after");Cevabı göster
arr[1] = 2 basılır. arr[3] istisna fırlatır: try'ın kalanı ATLANIR, eşleşen catch çalışır. finally her durumda çalışır. İstisna yakalandığı için program normal devam eder ve "after" basılır.
What is printed by trace(0); trace(1); trace(2);?
static void trace(int k) {
try {
System.out.print("A");
if (k == 1) throw new IllegalStateException();
if (k == 2) throw new IllegalArgumentException();
System.out.print("B");
} catch (IllegalStateException e) {
System.out.print("C");
} catch (RuntimeException e) {
System.out.print("D");
} finally {
System.out.print("E");
}
System.out.println("F");
}Cevabı göster
k = 0: istisna yok, A B, catch'ler atlanır, finally E, sonra F. k = 1: A, IllegalStateException ilk catch'e uyar: C, E, F. k = 2: A, IllegalArgumentException ilk catch'e uymaz, ikinci catch RuntimeException onun üst sınıfı: D, E, F. Catch'ler yukarıdan aşağı denenir, ilk uyan çalışır.
What is printed by level1();?
static void level3() {
System.out.println("level3 start");
int[] a = new int[2];
a[5] = 1;
System.out.println("level3 end");
}
static void level2() {
System.out.println("level2 start");
try { level3(); }
finally { System.out.println("level2 finally"); }
System.out.println("level2 end");
}
static void level1() {
System.out.println("level1 start");
try { level2(); }
catch (ArrayIndexOutOfBoundsException e) {
System.out.println("level1 caught: " + e.getMessage());
}
System.out.println("level1 end");
}Cevabı göster
Stack unwinding: level3'te istisna olur, orada catch yok, metot hemen biter ("level3 end" basılmaz). level2'de catch yok ama finally var: önce finally çalışır, sonra istisna yukarı devam eder ("level2 end" basılmaz). level1'de yakalanır, mesaj basılır, level1 normal devam eder.
What do f() and h() return, and what is printed?
static int f() {
try { return 1; }
finally { System.out.println("finally in f"); }
}
static int h() {
int x = 10;
try { return x; }
finally { x = 20; }
}
// in main:
System.out.println(f());
System.out.println(h());Cevabı göster
return'den önce bile finally çalışır: önce "finally in f", sonra dönen 1 basılır. h'de return x ifadesi değeri (10) o anda hesaplayıp saklar; finally'deki x = 20 saklanmış dönüş değerini değiştirmez: 10. (finally içine kendi return'ünü yazsaydın o kazanırdı, bu kötü bir alışkanlık.)
Find the compile error.
static void open(String name) {
throw new FileNotFoundException(name);
}Cevabı göster
FileNotFoundException, IOException'ın alt sınıfı, yani checked. javac: unreported exception FileNotFoundException; must be caught or declared to be thrown. Çözüm: imzaya throws FileNotFoundException ekle ya da try/catch ile sar.
Find the compile error.
try {
int x = Integer.parseInt("12");
} catch (Exception e) {
System.out.println("general");
} catch (NumberFormatException e) {
System.out.println("number");
}Cevabı göster
Exception her şeyi yakaladığı için ikinci catch'e hiçbir zaman sıra gelmez. javac: exception NumberFormatException has already been caught. Kural: özel (alt sınıf) catch'ler önce, genel olanlar sonra.
Find the compile error.
try {
System.out.println("hi");
} catch (IOException e) {
System.out.println("io");
}Cevabı göster
Checked bir istisnayı, try içinde onu fırlatabilecek bir şey yoksa yakalayamazsın. javac: exception IOException is never thrown in body of corresponding try statement. Bu kural unchecked istisnalar ve Exception için geçmez: catch (Exception e) burada derlenirdi.
Which exceptions are checked? (1) ArithmeticException (2) IOException (3) NullPointerException (4) FileNotFoundException (5) NumberFormatException
Cevabı göster
RuntimeException'dan türeyenler unchecked: ArithmeticException, NullPointerException, NumberFormatException (IllegalArgumentException'ın alt sınıfı), ArrayIndexOutOfBoundsException, ClassCastException. IOException ve alt sınıfı FileNotFoundException doğrudan Exception'dan gelir: checked, derleyici yakalamanı ya da bildirmeni ister.
What is printed?
Exception ex = new IllegalArgumentException("bad value");
System.out.println(ex.getMessage());
System.out.println(ex);
System.out.println(new RuntimeException().getMessage());Cevabı göster
getMessage sadece mesajı verir. toString (println'in çağırdığı) tam sınıf adı + ": " + mesaj verir. Mesajsız yaratılan istisnanın getMessage'ı null döner.
What is printed?
static void parse(String s) {
try {
int n = Integer.parseInt(s);
System.out.println(100 / n);
} catch (NumberFormatException | ArithmeticException e) {
System.out.println("bad input: " + e.getClass().getSimpleName());
}
}
// in main:
parse("4"); parse("x"); parse("0");Cevabı göster
Multi-catch tek blokta birden fazla tipi yakalar. "4": 100 / 4 = 25. "x": parseInt NumberFormatException. "0": parse başarılı ama 100 / 0 ArithmeticException. Multi-catch'teki tipler birbirinin alt sınıfı olamaz; ArithmeticException | RuntimeException derlenmez.
What is printed?
class InsufficientFundsException extends Exception {
private final double shortfall;
InsufficientFundsException(String msg, double shortfall) {
super(msg); this.shortfall = shortfall;
}
double getShortfall() { return shortfall; }
}
// Account.withdraw throws it when amount > balance, otherwise subtracts
Account acc = new Account(100);
try {
acc.withdraw(30);
acc.withdraw(100);
System.out.println("not printed");
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage() + ", short by " + e.getShortfall());
}
System.out.println(acc.getBalance());Cevabı göster
İlk çekim başarılı, bakiye 70. İkinci çekimde 100 > 70: istisna fırlar, bakiye değişmez, "not printed" atlanır. Eksik miktar 100 − 70 = 30.0. super(msg) mesajı Exception'a iletir, getMessage onu döner.
Write a checked exception InvalidAgeException and a method int readAge(String text) that parses text (ignoring surrounding spaces) and throws InvalidAgeException with message not a number: <text> if it is not an integer, or out of range: <age> if it is outside 0 to 150.
Cevabı göster
Checked istisna için Exception'dan türet. parseInt'in unchecked istisnasını yakalayıp anlamlı bir checked istisnaya çevir:
public class InvalidAgeException extends Exception {
public InvalidAgeException(String msg) { super(msg); }
}public static int readAge(String text) throws InvalidAgeException {
int age;
try {
age = Integer.parseInt(text.trim());
} catch (NumberFormatException e) {
throw new InvalidAgeException("not a number: " + text);
}
if (age < 0 || age > 150)
throw new InvalidAgeException("out of range: " + age);
return age;
}"21" → 21, " 40 " → 40, "abc" → not a number: abc, "200" → out of range: 200. Çağıran yer try/catch yazmak zorunda; yazmazsa unreported exception InvalidAgeException.Doğrulama: Tüm kod örnekleri ve cevaplar JDK 26 ile çalıştırıldı (java verify/SEN1002/W09.java; yakalanmayan istisna çıktısı da aynı dosyanın sonundan); derleme hataları javac ile denendi (verify/SEN1002/err/E09a-f.java).