Try-Catch-Finally Ve Exception Hiyerarşisi
03.Apr.2022Uygulamalarda bazen beklediğimiz bazen de beklemediğimiz problemler oluşabilir. Java’da bu tip hataları yönetmek için try-catch adlı bloklardan yararlanırız.
Bu uygulamamızda en temelden başlayıp ileri seviyeye kadar getireceğiz.
Hata ile ilgili konuşalım. Örneğin elimizde bir String olsun.
Yukarıdaki kod hiçbir zaman çalışmayacaktır. Bu dil bazında bir hatadır. Bu kod compile edilemez. Yani bytecode’a çevirilemez.
Fakat try-catch kullanırken yöneteceğimiz hatalar yukarıdaki örnekteki gibi olmayacaktır. Bizim senaryolarımız uygulama build edilip, bytecode’a çevirildiği zaman yakalayabileceğimiz hatalar olacaktır. Bunun için de try-catch kullanırız.
Bir Integer Array yazalım.
Yukarıdaki Array çıktısında oluşturmuş olduğumuz array’in 0. indisini bastırıyoruz. Alacağımız çıktı 1 olacaktır.
Biz bu kodu değiştirip 5. indisi yazdırmaya çalıştığımızda compiler bize bir hata vermeyecektir. Fakat biz bunun bir hata olduğunu biliyoruz. Çünkü bu dizinin 5. indisi yoktur. Dolayısıyla uygulamayı çalıştırdığımızda Java bize bir Exception fırlatacaktır. Bununla beraber “ArrayIndexOutOfBoundsException” şeklinde bir mesaj da alırız.
Bu noktada yazdığımız kodları bizim hata yönetimi şeklinde beslememiz gerekiyor.
Yukarıdaki try-catch blokları bir standarttır. Try ya da catch bloğu tek başına bir anlam ifade etmeyecektir. Catch bloğunun içerisi genellikle “Exception” olarak geçilir. Bunun anlamı try bloğu çalışmaya çalışır. Eğer kodlardan birinden hata fırlatılırsa catch bloğuna düşer. Az önceki “ArrayIndexOutOfBoundsException” adlı aldığımız hata da, catch bloğundaki “Exception” sınıfına parametre olarak geçilir. Yani uygulama tabiri caizse kırılmaz. Bu noktada yukarıdaki kod parçalarında ya try ya da catch en az bir kere çalışacaktır.
Biz bu uygulamada “Hata Oluştu” mesajı verdiğimiz gibi hatanın kendisini de bastırabiliriz. Yukarıdaki yazmış olduğumuz kodun catch bloğunu aşağıdaki gibi revize edersek istediğimiz sonuca ulaşmış oluruz.
Bu kod parçası ile birlikte fırlatılan hatanın catch bloğuna parametre olarak geçildiğinin ispatı niteliğinde olacaktır.
Uygulama çalıştırıldığında alacağımız hata yine “ArrayIndexOutOfBoundsException” şeklinde olacaktır.
Finally
Bir de finally bloğumuz vardır. Türkçesi “en sonunda” anlamı taşır. Bu blok ister try ister catch çalışsın, finally bloğu her türlü çalışır.
Yukarıdaki kodu yenileyelim.
Senaryomuzda bilindiği gibi 5. indise erişmeye çalışıyoruz. Bu durumda da bir Exception bize fırlatılıyor ve catch bloğuna düşüyor. Daha sonra finally bloğu çalıştı ve “Finally Bloğu Her Zaman Çalışır!” mesajını vermiş oldu.
Bu sefer try bloğunda 2. indise erişmeye çalışıyoruz. Bu sefer catch bloğu çalışmıyor ve ona rağmen yine finally bloğu çalışarak “Finally Bloğu Her Zaman Çalışır!” mesajını verecektir.
Bu blok gerçek senaryolarda stream, veri tabanı gibi bağlantıların yönetiminde oldukça fazla kullanılır.
Örnek bir konsept olarak veri tabanı bağlantısını ele alalım. Veri tabanına bağlanıp operasyonların bitiminde bu bağlantıyı kapatmamız gerekir. Bu durum hata gerçekleşse de gerçekleşmese de uygulanmalıdır.
Bu yaklaşım nesneye yönelik programlama dillerinin tamamında mevcuttur.
Exception Hiyerarşisi
Try-Catch-Finally yapısını daha iyi anlamak için hiyerarşiye de hakim olmak gerekir. Nesneye yönelik programlama dillerinde hiyerarşi genel olarak değişmemektedir. Elbette dilden dile klasifikasyonlar değişebilir ancak bu makalemizde Java hakkında konuşacağız.
Aşağıdaki görseli inceleyelim.
Exceptionlar ve Error sınıfları, Throwable isimli sınıftan inherit edilir. Exceptionlar çalıştırılan platformdan bağımsız yazdığımız kodlara yönelik hatalardır. Ancak Error sınıfı ise programcının inisiyatifinde olmayan ve tamamen işletim sisteminin yönetimiyle alakalı alınabilecek hatalardır. Exceptionlar ise kodlar ile kontrol edilebilir. Bunlar da tamamiyle try-catch yapılarıyla yönetilir.
Bizim ilgi alanımız görseldeki Exception hiyerarşisidir.
Görseli incelediğimizde yeni bir ayrım görüyoruz. Exception sınıfının altında RuntimeException, IOException, SQLException ve AWTException sınıflarını görüyoruz.
“IOException” : Stream Input/Output (Dosya İşlemleri) sırasında alacağımız hatalar.
“SQLException” : Veritabanı bağlantı veya sorgulama esnasında alınacak hatalar. v.s. v.s.
Baktığımızda bütün bu sınıflar Exception sınıfının child’ı konumundadır. RunTimeException’ a da dikkat edersek az önceki örneğimizde almış olduğumuz “ArrayIndexOutOfBoundsException” hatasını görebiliriz.
Burada okumamız ve görmemiz gereken şey aldığımız hataların hiyerarşik bir şekilde inherit edildiğidir. Yani kalıtım ilişkisidir.
“ArithmeticException” : Sayısal hatalar, belirlenmiş olan sayısal değerin dışına çıkıldığında fırlatılabilir.
“NullPointerException” : Sık sık karşılaşılan hatalardan biridir. Referansı olmayan classlar kullanılmaya çalışıldığında fırlatılır.
“ClassCastException” : Tip dönüşüm hatalardır. Birbirine çevrilemeyen ya da atama yapılamayan hatalardır.
“IndexOutOfBoundsException” : Arrayler ile ilgili hatalardır. Az önceki örneğimizde açıklaması yapılmıştır.
Bu saydığımız 4 Exception ve saymadığımız diğer 2 Exception sınıfı, “RunTimeException”‘dan extends edilir.
Bu 6 Exception sınıfı, diğer sınıflardan farklı olarak Unchecked Exception olarak isimlendirilir. Java Exception’ları check etmeyerek programcıya bu görevi yükler. Dolayısıyla bu Exception’ları try-catch ile kontrol altında tutmamız gerekir. Ama diğerleri örn: “IOException, SQLException, AWTException” unchecked exception’dan farklı olarak Check Exception olarak isimlendirilir. Bu sınıfların özel durumu vardır. Java bize yukarıdaki Exception’ların oluşabileceği noktalarda uyarı verir ve”Handle” edilmesini ister.