BÖLÜM 8

 

Altuğ B. Altıntaş

© 2004

 

 

 

 

 

İstisnalar (Exception)

“Diğerlerinin yazdığı  programda hata olabilir ama benim yazdığım programda hata olmaz....“ - Anonim

 

Bu bölümde istisnalar üzerinde durulacaktır. İstisna deyince aklınıza ne geliyor? Yanlış yazılmış uygulama mı? Beklenmeyen durum mu? Yoksa her ikisi de mi? İstisna demek işlerin sizin kontrolünüzden çıkması anlamına gelir. Yani kaos ortamı, önceden kestirilemeyen... Birşeylerin ters gitmesi sonucu uygulamanın normal akışına devam edememesi demektir. Bu ters giden bir şeyler ne olabilir? Örneğin kullanıcının uygulamanıza istemeyen veri girmesi olabilir veya açmak istediğiniz dosyanın yerinde olmaması olabilir, örnekleri çoğaltmak mümkündür. (yorum ekle)

 

8.1. İstisnalara Giriş

Gerçekten tam bir uygulama yazmak ne demektir? Uygulamadan beklenen görevleri yerine getirmesi onu tam bir uygulama yapar mı? Tabii ki yapmaz. Uygulama zaten kendisinden beklenen işi yapmalı, aksi takdirde zaten uygulama olmaz. Bir uygulamanın tam olmasının iki şartı vardır; Birincisi uygulamanın kendisinden beklenen görevleri doğru bir şekilde yerine getirmesidir yani doğruluk, ikincisi ise hatalı davranışlara karşı dayanıklı olmasıdır, sağlamlık. Örneğin bizden iki sayıyı bölmek için bir uygulama istense ne yapılmalıdır, A/ B - A bölüm B çok basit değil mi?. İlk etapta karşı tarafın bizden istediği şey, girilen iki sayının doğru şekilde bölünmesidir - doğruluk, bu öncelikli şarttır, bunda herkes hemfikir. Peki ikinci şart nedir? İkinci şart ise sağlamlıktır, ikinci şart olan sağlamlık genellikle önemsenmez. Bu örneğimizde karşı tarafın bizden istediği olay, iki sayının bölünmesidir ama dikkat edin sayı dedim, kullanıcı int, double veya short ilkel tiplerinde sayı girilebilir. Peki ya kullanıcı String bir ifadeyi uygulamanıza yollarsa ne olur? veya A=5, B=0 girince uygulamanız buna nasıl bir tepki verir? (Not :5/0=sonsuz) Uygulamanız direk olarak kapanır mı? Veya uygulamanız bu anlamsız ifadeleri bölmeye mi çalışır? Eğer siz uygulamayı tasarlayan kişi olarak, bu hataları önceden tahmin etmiş ve önlemleri almışsanız sorun ortaya çıksa bile, uygulama için sorun olmaz ama gerçek dünyada her şeyi öngörebilmek imkansızdır.  (yorum ekle)

Java programlama dili, oluşabilecek hatalara karşı sert bir yaptırım uygular. Dikkat edin, oluşabilecek diyorum. Java programlama dili, ortada hata oluşmasına sebebiyet verebilecek bir durum var ise yazılan Java dosyasını derlemeyerek (compile) kodu yazan kişiye gerekli sert tavrı gösterir. Java programlama dilinin bu tavrı doğru mudur? Kimileriniz diyebilir ki, "Java sadece üstüne düşen görevi yapsın, oluşabilecek hataları bana söyleyerek canımı sıkmasın". Bu yaklaşım yanlıştır, Java programlama dilinin amacı kodu yazan kişiye maksimum şekilde yardımcı olmaktır, daha doğrusu insana dayalı oluşabilecek hataları kendi üstüne alıp, hatalı uygulama üretimini minimuma indirgemeyi amaçlayarak tasarlanmıştır. Bunun ilk örneğini çöp toplama (garbage collector) mekanizmasında görmüştük. Diğer dillerde oluşturulan nesnelerin, daha sonradan işleri bitince bellekten silinmemelerinden dolayı bellek yetmezlikleri oluşmaktadır. " Kodu yazan insan, oluşturduğu nesneyi bellekten temizlemez mi? Ben bunu şahsen hiç yapmam. O zaman dalgın insanlar kod yazmasın aaa!  " diye bir söz sakın demeyin, çünkü insanoğlu yeri geldiğinde çok dalgın olabilir ve bu dalgınlık uygulamayı bir bellek canavarına dönüştürebilir ayrıca bu tür hataları, uygulamanın içerisinden ayıklamak cidden çok zor bir iştir. Bu yüzden Java programlama dilinde, bir nesnenin bellekten silinmesi kodu yazan kişiye göre değil, çöp toplama algoritmalarına göre yapılır (bkz:Bölüm3). Java’nın oluşabilecek olan hatalara karşı bu sert tutumu da gayet mantıklıdır. Bu sert tutum sayesinde ileride oluşabilecek ve bulunması çok güç olan hataların erkenden engellenmesini sağlar. (yorum ekle)

 

8.1.1. İstisna Nasıl Oluşabilir?

İstisna oluşumuna en basit örnek olarak, yanlış kullanılmış dizi uygulamasını verebiliriz. Java programlama dilinde dizilere erişim her zaman kontrollüdür. Bunun anlamı, Java programlama dilinde dizilerin içerisine bir eleman atmak istiyorsak veya var olan bir elemana ulaşmak istiyorsak, bu işlemlerin hepsi Java tarafından önce bir kontrolden geçirilir. Bunun bir avantajı, bir de dezavantajı vardır. Avantaj olarak güvenli bir dizi erişim mekanizmasına sahip oluruz, dezavantaj olarak ufakta olsa hız kaybı meydana gelir. Fakat böyle bir durumda hız mı daha önemlidir yoksa güvenlik mi? Bu sorunun cevabı Java programlama dili için güvenliktir. Aşağıdaki örneğe dikkat edelim; (yorum ekle)

 Örnek:  DiziErisim.java (yorum ekle)

 
public class DiziErisim {
 
   public static void main(String args[]) {
 
     int sayilar[] = {1, 2, 3, 4};
     System.out.println("Basla");
     for (int i=0 ; i < 5 ; i++) {
         System.out.println("--> " + sayilar[i]);
     } 
     System.out.println("Bitti");
  }
}
 

 

sayilar[], ilkel (primitive) int tipinde dizi değişkenidir ve bağlı bulunduğu dizi nesnesinin içerisinde 4 adet int tipinde eleman vardır. for döngüsü sayesinde dizi içerisindeki elemanları ekrana bastırmaktayız. Bu örneğimizdeki hata, for döngüsünün fazla dönmesiyle dizinin olmayan elemanına ulaşmak istememizden kaynaklanmaktadır. Böyle bir hareket, çalışma-anında (run-time) hata oluşmasına sebebiyet verip uygulamamızın aniden sonlanmasına sebebiyet verecektir. Uygulamayı çalıştırıp, sonuçları hep beraber görelim.  (yorum ekle)

 
Basla
--> 1
--> 2
--> 3
--> 4
Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException
    at DiziErisim.main(DiziErisim.java:10)
 

Bu örneğimizdeki istisna, ArrayIndexOutOfBoundsException istisnasıdır. Bu istisnanın sebebi, bir dizinin olmayan elemanına erişmeye çalıştığımızı ifade eder. Fark edildiği üzere Java programlama dilinde, oluşan istisnaları anlamak ve yerlerini belirlemek çok zor değildir. Örneğin bu uygulamada istisnanın 10. satırda ortaya çıktığı anlaşılabilmektedir. (yorum ekle)

 

8.1.2. Başka İstisnalar Neler Olabilir? 

Bir uygulama içerisinde, başka ne tür istisnalar oluşabilir ? Bir kaç örnek verirsek;

 

·       Açmak istediğiniz fiziksel dosya yerinde olmayabilir. (yorum ekle)

·       Uygulamanıza kullanıcılar tarafında, beklenmedik bir girdi kümesi gelebilir. (yorum ekle)

·       Ağ bağlantısı kopmuş olabilir. (yorum ekle)

·       Yazmak istediğiniz dosya, başkası tarafından açılmış olduğundan yazma hakkınız olmayabilir. (yorum ekle)

 

Olabilir, olmayabilir, belki... Yukarıdaki istisnaların,  bir uygulamanın başına gelmeyeceğini kim garanti edebilir? Kimse, peki Java program içerisinde tam bir uygulama nasıl yazılır. Başlayalım... (yorum ekle)

 

8.1.3. İstisna Yakalama Mekanizması

Bir istisna oluştuğu zaman uygulamamız aniden kapanmak zorunda mı? Oluşan bu istisnayı daha şık bir şekilde yakalayıp uygulamanın devam etmesini sağlamak mümkün mü? Cevap olarak evet; (yorum ekle)

 

Gösterim-8.1:

 
try {
  // Istisnaya sebebiyet verebilecek olan kod
} catch(Exception1 e1) {
  //Eger Exception1 tipinde istisna firlatilirsa buraya 
} catch(Exception2 e2) {
  //Eger Exception2 tipinde istisna firlatilirsa buraya
}
 

 

İstisnaya sebebiyet verebilecek olan kod, try bloğunun içerisinde tutularak güvenlik altına alınmış olur. Eğer istisna oluşursa, istisna yakalama mekanizması devreye girer ve  oluşan bu istinanın tipine göre, uygulamanın akışı catch bloklarından birinin içerisine yönlenerek devam eder. (yorum ekle)

İstisnalar nesnedir. Bir istisna oluştuğu zaman bir çok olay gerçekleşir. İlk önce yeni bir istisna nesnesi belleğin heap alında new() anahtar kelimesi ile oluşturulur. Oluşan bu istisna nesnesinin içerisine hatanın oluştuğu satır yerleştirilir. Uygulamanın normal seyri durur ve oluşan bu istisnanın yakalanması için catch bloğunun olup olmadığına bakılır. Eğer catch bloğu varsa uygulamanın akışı uygun catch bloğunun içerisinden devam eder. Eğer catch bloğu tanımlanmamış ise hatanın oluştuğu yordamı (method)cağıran yordama istisna nesnesi paslanır, eğer bu yordam içerisinde de istisnayı yakalamak için catch bloğu tanımlanmamış ise istina nesnesi bir üst yordama paslanır, bu olay böyle devam eder ve en sonunda main() yordamına ulaşan istisna nesnesi için bir catch bloğu aranır eğer bu yordamın içerisinde de catch bloğu tanımlanmamış ise, uygulananın akışı sonlanır. Bu olayları detaylı incelemeden evvel temel bir giriş yapalım; (yorum ekle)

 Örnek:  DiziErisim2.java (yorum ekle)

 
public class DiziErisim2 {
 
  public void calis() {
 
    int sayilar[] = {1,2,3,4};
    for (int i=0 ; i < 5 ; i++) {
      try {
        System.out.println("--> " + sayilar[i]);
      } catch (ArrayIndexOutOfBoundsException ex) {
        System.out.println("Hata Olustu " + ex);
      } 
    } // for
  }
 
  public static void main(String args[]) {
 
    System.out.println("Basla");
    DiziErisim2 de2 = new DiziErisim2();
    de2.calis();
    System.out.println("Bitti");
  }
}
 

 

Yukarıdaki uygulamamızda, dizi elemanlarına erişen kodu try bloğu içerisine alarak, oluşabilecek olan istinaları yakalama şansına sahip olduk. Sahip olduk da ne oldu diyenler için gereken açıklamayı hemen yapalım. try-catch istisna yakalama mekanizması sayesinde istisna oluşsa bile uygulamanın akışı aniden sonlanmayacaktır. DiziErisim.java  ile  DiziErisim2.java uygulamalarının çıktısına bakılırsa aradaki kontrolü hemen fark edilecektir. DiziErisim2.java uygulama örneğimizin çıktısı aşağıdaki gibidir. (yorum ekle)

 
Basla
--> 1
--> 2
--> 3
--> 4
Hata Olustu java.lang.ArrayIndexOutOfBoundsException
Bitti
 

Kontrol nerede? Yukarıdaki DiziErisim2.java uygulamasının çıktısının son satırına dikkat ederseniz, "Bitti" yazısının ekrana yazıldığını görürsünüz oysaki bu ifade DiziErisim.java uygulamasının çıktısında görememiştik. İşte kontrol buradadır. Birinci kuralı daha net bir şekilde ifade edersek; try-catch istisna yakalama mekanizması sayesinde, istisna oluşsa bile uygulamanın akışı aniden sonlanmaz. (yorum ekle)

Yukarıdaki örneğimizde, try-catch mekanizmasını for döngüsünün içerisine koyulabileceği gibi, for döngüsünü kapsayacak şekilde de tasarlanıp yerleştirilebilir. (yorum ekle)

 

Örnek:  DiziErisim3.java (yorum ekle)

 
public class DiziErisim3 {
 
  public void calis() {
    try {
      int sayilar[] = {1,2,3,4};
      for (int i=0 ; i < 5 ; i++) {
        System.out.println("--> " + sayilar[i]);
      }
    } catch (ArrayIndexOutOfBoundsException ex) {
        System.out.println("Hata Yakalandi");
    }
 
  }
 
  public static void main(String args[]) {
 
    System.out.println("Basla");
    DiziErisim3 de3 = new DiziErisim3();
    de3.calis();
    System.out.println("Bitti");
  }
}
 

 

Bu uygulama örneği ile DiziErisim2.java örneğimiz arasında sonuç bakımından bir fark yoktur. Değişen sadece tasarımdır, try-catch bloğunun daha fazla kodu kapsamasıdır. (yorum ekle)

 

8.1.4. İstisna İfadeleri

Bir yordam hangi tür istisna fırlatabileceğini önceden belirtebilir veya belirtmek zorunda kalabilir. Bu yordamı (method) çağıran diğer yordamlar da, fırlatılabilecek olan bu istisnayı, ya yakalarlar ya da bir üst bölüme iletirler. Bir üst bölümden kasıt edilen, bir yordamı çağıran diğer bir yordamdur. Şimdi bir yordamın önceden hangi tür istisna fırlatacağını nasıl belirtmek zorunda kaldığını inceleyelim. (yorum ekle)

 

Örnek:  IstisnaOrnek1.java (yorum ekle)

 
import java.io.*;
 
public class IstisnaOrnek1 {
 
  public void cokCalis() {
    File f = new File("ornek.txt");
    BufferedReader bf = new BufferedReader( new FileReader( f ) );
    System.out.println(bf.readLine());
  }
 
  public void calis() {
    cokCalis();
  }
 
  public static void main(String args[]) {
    IstisnaOrnek1 io1 = new IstisnaOrnek1();
    io1.calis();
  }
}
 

 

java.io paketinin içerisindeki sınıfları henüz incelemedik ama bu örneğimizde kullanılan sınıfların ne iş yaptıklarını anlamak çok zor değil. Burada yapılan iş, aynı dizinde bulunduğu farz edilen ornek.txt dosyasının ilk satırını okumaya çalışmaktır. Yukarıdaki uygulamamızı derlemeye çalışırsak, derleyicinin bize vereceği mesaj aşağıdaki gibi olur. (yorum ekle)

 
IstisnaOrnek1.java:9: unreported exception
java.io.FileNotFoundException; 
must be caught or declared to be thrown new FileReader(f));
                                         ^
IstisnaOrnek1.java:10: unreported exception java.io.IOException; 
must be caught or declared to be thrown 
System.out.println(bf.readLine());
                                                              ^
2 errors
 

Biz diskimizde bulunduğu varsayılan bir dosyaya erişip onun ilk satırını okumaya çalışmaktayız. Çok masum gibi gözüken ama tehlikeli istekler. Peki daha detaylı düşünelim ve oluşabilecek olan istisnaları tahmin etmeye çalışalım.  (yorum ekle)

İlk oluşabilecek olan istisna, o dosyanın yerinde olmayabileceğidir. Bu beklenmeyen bir durum oluşturabilir, başka neler olabilir? Bundan ayrı olarak biz sanki o dosyanın orada olduğundan eminmişiz gibi birde onun ilk satırını okumaya çalışıyoruz, bu isteğimizde istisnaya sebebiyet verebilir çünkü dosya yerinde olsa bile dosyanın ilk satırı olmayabilir. Dikkat ederseniz hep olasılıklar üzerinde durmaktayım ama güçlü olasılıklar. Peki bu uygulamayı derlemenin bir yolu yok mu?  (yorum ekle)

Az önce bahsedildiği gibi bir yordam içerisinde oluşmuş olan istisnayı bir üst bölüme yani o yordamı çağıran yordama fırlatabilir. Eğer bir istisna oluşursa bu anlattıklarımıza göre bir yordamın iki şansı vardır diyebiliriz. Birincisi oluşan bu istisnayı ya yakalayıp gereken işlemleri kendi içerisinde sessizce gerçekleştirebilir veya bu istisna ile ben ne yapacağımı bilmiyorum beni çağıran yordam düşünsün diyip, istisna nesnesini bir üst bölüme fırlatabilir. (yorum ekle)

Aşağıdaki örnekte, oluşan istisnayı aynı yordamın içerisinde yakalanmaktadır; bu yüzden yordamın hangi istisnayı fırlatabileceğini açıklamasına gerek yoktur. Bir yordamın hangi tür istisnayı nasıl fırlatabileceğini açıklama olayını az sonra göreceğiz ama önce aşağıdaki örneğimizi inceleyelim.  (yorum ekle)

 

Örnek:  IstisnaOrnek2.java (yorum ekle)

 
import java.io.*;
 
public class IstisnaOrnek2 {
 
  public void cokCalis() {
    try {
      File f = new File("ornek.txt");
      BufferedReader bf=new BufferedReader(new FileReader(f) );
      System.out.println(bf.readLine());
    } catch (IOException ex) {
      System.out.println("Hata Yakalandi =" + ex);
    } 
  }
 
  public void calis() {
    cokCalis();
    System.out.println("calis() yordamı");
  }
 
  public static void main(String args[]) {
    IstisnaOrnek2 io2 = new IstisnaOrnek2();
    io2.calis();
    System.out.println("main() yordamı");
  }
}
 

 

Verilen örnekte, dosyaya erişirken veya ondan birşeyler okumak isterken oluşabilecek olan istisnalar; java.io.IOException istisna tipini kullanarak yakalanabilir. Zaten IstisnaOrnek1.java uygulamasının derlemeye çalışırken alınan hatadan hangi tür istisna tipinin kullanılması gerektiğini de çıkartabiliriz. java.io.FileNotFound Exception istina tipini, java.io.IOException tipi kullanılarak yakalanabilir bunun nasıl olduğunu biraz sonra göreceğiz. (yorum ekle)

Yukarıdaki uygulama güzel bir şekilde derlenir çünkü oluşabilecek olan tüm istisnalar için tedbir alınmıştır. Olayların akışını inceliyelim, bu uygulamayı çalıştırdığımız zaman (java IstisnaOrnek2) ilk olarak main() yordamından akışa başlanır. Daha sonra  calis() yordamının ve  cokCalis()