Programlama dillerinde hata yönetimi yapılırken ortaya çıkan beklenmedik hatalara exception (istisna) adı verilir. Bir exception gerçekleştiğinde ise o programın akışı bozulur ve algoritması tamamlanamadan sonlanır. Bir hatanın ortaya çıkabilmesi çeşitli sebeplerden gerçekleşebilir:
- Kullanıcının hatalı bir giriş yapması (sayı olması gereken bir yere harf yazabilir ya da bir tarih yazarken ay ile gün sırasını karıştırarak valid olmayan bir tarih yazabilir)
- Programın akışı esnasında veritabanı kullanılıyorsa veritabanı sunucusunda bir bağlantı problemi olabilir ya da programın erişmesi gereken (yazma ya da okuma yapmak için) bir dosya yerinde bulunamayabilir
- Kullanılan programın tükettiği bellek ihtiyacı mevcut belleğin üzerine çıkabilir
Bu gibi hataların yol açabileceği beklenmedik akış sonlanmaları yaşamamak için bazı kod bloklarının kontrollü olarak çalıştırılması sağlanır ve bu kontrollü yapılar içerisinde oluşabilecek hatalarda akış kontrollü olarak bir sonraki istenen noktaya aktarılarak programın erkenden sonlanması engellenmiş olur. Bu kontrollü ifadelere try catch blokları adı verilir ve hemen tüm progamlama dillerinde de kullanımları oldukça benzerdir.
try {
// Kontrollü kod çalıştırma alanı
} catch (ExceptionName e1) {
// try boğu içerisinde bir hata oluşması durumunda çalışacak kod
}
Bu şekilde iç içe try catch blokları hazırlayarak farklı sebeplerden ortaya çıkabilecek hatalar için b, c... planları yapmak da mümkündür.
Bir program kodunun çalıştırılmasında, yukarıda yazdığım gibi birden çok farklı sebeple hata ortaya çıkabilir. Ancak her hata durumunda farklı bir b planı devreye sokmak gerekebileceği için alınan hata kodunun türüne göre farklı kaçış planları da hazırlanabilir:
try {
// Kontrollü kod çalıştırma alanı
} catch (ExceptionTuru1 exp1) {
// Bu tür bir hata gerçekleşirse bu kodu çalıştır
} catch (ExceptionTuru2 exp2) {
// Bu tür bir hata gerçekleşirse bu kodu çalıştır
} catch (ExceptionTuru3 exp3) {
// Bu tür bir hata gerçekleşirse bu kodu çalıştır
}
Şimdi try catch bloğu kullanmadan hatalı olduğunu bildiğimiz bir programı çalıştıralım ve sonucunu gözlemleyelim:
int a=1;
int b=0;
int c=a/b;
Çıktı: Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.main(Main.java:5)
Bu kod hata verecektir çünkü bir sayının sıfıra bölümü hata üretir (hata türü de yazıyor: java.lang.ArithmeticException). Bu 3 satırlık kodun devasa bir projenin içinde geçtiğini ve b'nin 0 olduğunu bir düşünün, koca bir proje bir anda bu saçma hatadan dolayı sonlanacak ve çıktı ekranında da bu çirkin mesaj görüntülenecektir.
Öncelikle try catch blokları içinde hata almamız durumunda hata mesajını daha az çirkin bir şekilde nasıl alabileceğimize bakalım. Hatanın ne olduğunu da bilmek durumundayız çünkü başka türlü düzeltmemiz mümkün olmaz. Bir sonraki aşamada bir projenin çalışması esnasında oluşan tüm hataların log'lanarak saklanmasını sağlayacağız, bunun için yine hataları yakalayabiliyor olmamız gerek.
Java'da Try Catch Kullanımı Örneği
try
{
int a=1;
int b=0;
int c=a/b;
}
catch (ArithmeticException exception)
{
System.out.println("Beklenmeyen bir sorun oluştu: ");
exception.printStackTrace();
}
System.out.println("Programın devamı");
Çıktı:
Beklenmeyen bir sorun oluştu:
java.lang.ArithmeticException: / by zero
at Main.main(Main.java:7)
Programın devamı
Kodlarımızı bu şekilde güncellediğimizde 3 şey kazanmış oluyoruz:
1. Program, hata ürettiği noktada sonlanmıyor ve hatadan sonraki "Programın devamı" çıktısını da alabildiğine göre çalışmaya devam ediyor
2. Önceden aldığımız çirkin hata mesajı yerine daha görsel ve anlaşılabilir bir hata çıktısı verdi
3. Hatanın nerede olduğu ve ne sbeple hata verdiği konusunda bizi bilgilendirdi
Peki, madem try catch kullanmak bu kadar mantıklı bir şey, neden bütün programlarımızı kocaman try catch bloklarına almıyoruz? Çünkü maalesef try catch kullanmak zaman ve kaynak tüketimi açısından maliyetli bir işlemdir. Bu yüzden nerelerde hata oluşabileceğini iyi değerlendirmek gerekir. Mesela değerleri belli olan bir for döngüsü içerisinde çıktı aldığımız bir alanın hata üretemeyeceği nasıl olsa bellidir ve bu alanda çok değerli kaynaklarımızı boşuna tüketmemeliyiz, ancak özellikle kullanıcıdan ya da farklı bir veri bankasından veri alırken ya da yazarken hatalı yazımlar ile bağlantı problemleri oluşabileceğine dikkat etmeliyiz ve arayüz tarafında yeterli doğrulama (validasyon) yapma imkanımız yoksa bu alanları try catch blokları içine almalıyız.
Java'da Try-Catch-Finally Kullanımı
Peki hata olsa da olmasa da mutlaka çalışacağını bildiğimiz bir alan olsa nasıl olurdu. İşte try-catch bloğunun devamında yazılan finally bloğu da tam olarak bu işlevi yerine getirmektedir.
HATA VARSA:
try
{
int a=1;
int b=0;
int c=a/b;
}
catch (ArithmeticException exception)
{
System.out.println("Beklenmeyen bir sorun oluştu: ");
exception.printStackTrace();
}
finally
{
System.out.println("Burası her şartta çalışacak");
}
Çıktı:
Beklenmeyen bir sorun oluştu:
java.lang.ArithmeticException: / by zero
at Main.main(Main.java:7)
HATA YOKSA:
try
{
int a=1;
int b=1;
int c=a/b;
}
catch (ArithmeticException exception)
{
System.out.println("Beklenmeyen bir sorun oluştu: ");
exception.printStackTrace();
}
finally
{
System.out.println("Burası her şartta çalışacak");
}
Çıktı:
Burası her şartta çalışacak
Açıklama: Görüldüğü gibi hata olmazsa catch bloğu çalışmıyor, hata olursa da try tamamlanamıyor. Ama Finally bloğu her şartta kesin olarak çalışıyor.
Birden çok Türde Hata Yönetimi Yapmak
Peki yukarıdaki örneğimizde olası bir ArithmeticException oluşabileceğini öngörmüştük, ya birden fazla sebeple oluşabilecek bir hata için geçerli bir exception catch'i yazmamız gerekseydi? Aşağıdaki catch bloğu, hangi türde olursa olsun tüm hata türlerinde olan hataları yakalayacaktır:
try
{
int a=1;
int b=1;
int c=a/b;
}
catch (Exception exception)
{
System.out.println("Beklenmeyen bir sorun oluştu: ");
exception.printStackTrace();
}
NOT: Özel bir alanda oluşacak bir hata için farklı bir catch bloğu kullanmak için Hemen try bloğundan sonra yazılmalıdır, yoksa zaten genel Exception catch bloğuna takılacaktır.
Benden beklenmeyecek uzunlukta bir yazı oldu, açıkçası başlarken de bu kadar süreceğini öngörememiştim :) Konu başlığını hata yönetimi olarak açmış olsaydım throw/throws anahatr sözcüğünün kullanımını da buraya ekleyecektim ama try-catch olduğu için bu konuyu burada sonlandırmak daha mantıklı olacak. Bir sonraki yazımda da throws anahtar sözcüğünü örneklerle açıklamaya ve anlatmaya çalışacağım.