Java Exceptions
Exception(istisna), Java’nın oluşabilecek sorunlar karşında, müdahale edebilme yetisi kazandırmasıdır. Bu müdahale edebilme yetisini try, catch, finally anahtar kelimeleri ile kazanmaktayız. . Bir veya daha fazla catch ifadesinden ve finally ifadesinden oluşmaktadır. Aşağıdaki örnekte temel bir try-catch-finally bloğu gösterilmektedir. try bloğunda gerçekleştireceğimiz işleme dair kodlar yazılır. Bu işlemlerde bir istisna oluşursa catch bloğunun bulunup, bulunmadığına bakılır. Catch bloğu varsa burada istisna handle edilir.
try { //Kod } catch (IOException e) { // Exception yakala }catch (ArithmeticException | IllegalArgumentException e) { // Birden fazla exception yakala }finally { // try>/catch blokları tamamlandıktan sonra her zaman çalışır. }
Birden fazla catch bloğu olabilir, ancak sadece bir tane finally bloğu olabilir.
throw / throws
throw ifadesi kullanılarak bir istisnanın açıkça fırlatılmasına olanak sağlar. throws ise; bir metodun istisnayı atma potansiyeline sahip olduğunu ifade etmek için kullanılır. Örnek olarak aşağıdaki veritabaniOkulGetir() isimli metodun SQLException istisnası fırlatabileceğini ifade ettik. Metod içerisinde UnsupportedOperationException istisnasını throw ifadesi ile açık bir şekilde fırlattık.
public String veritabaniOkulGetir() throws SQLException{ //Declare exception throw new UnsupportedOperationException(); }
Exception Türleri
Java’da tüm exceptionlar throwable sınıfından türemiştir. İki tip exception bulunmaktadır. Bunlar checked exception ve unchecked exception. Aşağıdaki görselde bu hiyerarşi gösterilmektedir. Java’da exception sınıfından türeyen ancak RuntimeException sınıfından türemeyen tüm sınıflar checked exception olarak adlandırılmaktadır. Checked exceptionlar uygulama içinde handle edilebilmektedir. Unchecked exceptionlar runtime zamanda oluşan exceptionlardır. Unchecked exceptionların handle edilmesi ya da declare edilmesi gerekmez.

Temel Unchecked Exception Listesi
AritmeticException | ArrayIndexOutOfBoundsException |
ArrayStoreException | ClassCastException |
IllegalArgumentException | IllegalStateException |
MissingResourceException | NullPointerException |
NumberFormatException | UnsupportedOperationException |
Temel Checked Exception Listesi
FileNotFoundException | IOException |
NotSerializableException | ParseException |
SQLException |
Exception Sınıflarının Kalıtımı
Bazı durumları istisna sınıflarının kalıtımı önemli olabilir. Tüm exceptionlar, exception sınıfından türemiştir. NumberFormatException, IllegalArgumentExceptiondan türemiştir. Aynı şekilde; FileNotException ve NotSerializableException, IO Exceptiondan türemiştir. Bu durum çoklu exception yakalamak istediğimizde önemlidir. Aşağıdaki örnek derlenmez. Çünkü FileNotException, IOExceptionun alt sınıfı. Fazladan gereksiz exception bildirdik.
try { throw new IOException(); } catch (IOException | FileNotException e) { // DERLENMEZ. ÇÜNKÜ FileNotException, IOExceptionun alt sınıfı. // TODO: handle exception }
Özel Exception Oluşturma
Java’da kullanıma hazır bir çok istisna bulunmaktadır. Bunların dışında kendi istisnalarımızı oluşturabiliriz. Kendi istisnalarımızı oluştururken checked ya da unchecked exception olup olamayacağına karar vermemiz gerekmektedir. Oluşturacağımız istisnayı herhangi bir istisna sınıfından türetebiliriz. Yaygın olarak Exception ve RuntimeException sınıfından türetilmektedir. Aşağıdaki örnekte istisna sınıflarının türetilmesi örneklendirilmiştir.
class AException extends Exception{} class BException extends RuntimeException{} class CException extends BException{} class Practice { public void test() throws AException{ //kodumuz } }
İstisnaya Özel Constructor Ekleme
İstisna sınıfımıza constructor ekleyebiliriz. Aşağıdaki örnekte örneklendirilmiştir. İlk constructor, parametresi olmayan varsayılan default constructor’dur. İkinci istisna, başka bir istisnanın, oluşturulan yeni istisna içerisinde nasıl sarmalanacağını gösterir. Üçüncü constructor, özel bir hata mesajının nasıl iletileceğini gösterir.
public class ConstructorException extends Exception { public ConstructorException() { super(); //isteğe bağlı, olmasa da olur, derleyici otomatik ekleyecektir. } public ConstructorException(Exception e) { super(e); } public ConstructorException(String mesaj) { super(mesaj); } }
Stack Trace Yazdırma
Gösterdiğimiz hata mesajlarına stack-trace adı verilmektedir. JVM tarafından işlenmeyen bir istisna atıldığında otomatik olarak bir stack-trace yazdırır. Kendimiz de stack trace yazdırabiliriz:
try { throw new AException(); }catch (AException e) { e.printStackTrace(); }
Kaynak Yönetimini Otomatikleştirme
Java’da istisnaların yönetiminde ayrıca try-with-resources ifadesi bulunmaktadır. Bu yapı try bloğunda belirtilen tüm kaynakların try bloğu sonunda otomatik olarak kapatılmasını sağlar. Bu duruma otomatik kaynak yönetimi adı verilir. Java bütün kapatma işlemlerini bizim yerimize yapar. Bir kaynak, dosya okuma yazma işlemi ya da veri tabanı bağlantı işlemi gibi işlemler olabilir.
Kaynaklar otomatik olarak kapatılabilmesi için AutoCloseable interface’ini uygulamaları gerekmektedir. String ifadeler autoclosable interfacesini uygulamadığından kullanılamaz. AutoCloseable interface, close() adında bir metod barındırmaktadır. Otomatik sonlandırmayı bu metod sayesinde gerçekleştirmektedir. Bunun dışında Closeable interface‘i de bulunmaktadır. AutoCloseable interface, Closeable interface’den türemiştir, aralarındaki tek fark, closeable interface close metodunda IOException deklare ederken; autocloseble interface’i close metodunda Exception’ı deklare eder.
Aşağıdaki örnekte görüldüğü gibi, herhangi bir catch veya finally bloğu çalıştırılmadan önce, try ifadesinin sonunda kaynaklar kapatılır. Arka tarafta JVM gizli bir finally bloğunu close() metodunda tetikler.
public class DosyaOkuyucu implements AutoCloseable { private String isim; public DosyaOkuyucu(String isim) { this.isim = isim; } @Override public void close() { System.out.println("Kapat: "+ isim); } public static void main(String[] args) { try (var bookReader = new DosyaOkuyucu("kedi")) { System.out.println("Try"); }finally { System.out.println("Finally"); } } } //Çıktı: //Try //Kapat: Kedi //Finally
Bir try ifadesinden sonra birden fazla kaynak bildirilebilir. Bunlar noktalı virgül ile ayrılır. Kapatılma sırası, bildirildikleri sıranın tersi şeklindedir.
public class DosyaOkuyucu2 implements AutoCloseable { String isim; public DosyaOkuyucu2(String isim) { this.isim = isim; } @Override public void close() { System.out.println("Kapatildi: " + isim); } public static void main(String[] args) { try(var kitapOkuyucu = new DosyaOkuyucu2("1"); var filmOkuyucu = new DosyaOkuyucu2("2"); var muzikOkuyucu = new DosyaOkuyucu2("3")) { System.out.println("Try"); }finally { System.out.println("Finally"); } } }
Try ifadesi içinde belirtilen kaynaklar, try bloğu sona erdiğinde kullanılamazlar.
Java 9 ile beraber, try ifadesinden önce bildirilen kaynaklar final veya effectively final olarak işaretlenmiş olmaları koşulu ile kullanmak mümkündür.
final var kitapOkuyucu = new DosyaOkuyucu2("5"); DosyaOkuyucu2 filmOkuyucu = new DosyaOkuyucu2("3"); try (kitapOkuyucu ; filmOkuyucu) { System.out.println("Try"); } finally { System.out.println("Finally"); }
Suppressed Exceptions
try-with-resource ifadesinde close() metodunda bir istisna fırlattığımızı düşünelim. Try bloğunda da bir istisna fırlatırsak ne olacak? Birden fazla istisna atıldığında ilki dışındaki tüm istisnalara suppressed exception adı verilir. Aşağıdaki örnekte birincil istisna olarak 8. satırdaki istisna atılır. Geri kalan close() metodundaki istisna suppressed exception adı verilir.
public class Test implements AutoCloseable { public void close() throws IllegalStateException { throw new IllegalStateException("Kapali"); } public static void main(String[] args) { try(Test t = new Test()) { throw new IllegalStateException("Exp1"); }catch (IllegalStateException e) { System.out.println("Caught: "+e.getMessage()); for(Throwable t: e.getSuppressed()) { System.out.println("Suppressed: "+ t.getMessage()); } } } } //Caught: Exp1 //Suppressed: Kapali