~BöLÜM-8~

Altuğ B. Altıntaş

©2002

İstisnalar (Exceptions)

“Diğerlerinin yazdığı  programda hata olabilir ama benim yazdığım programda hata olmaz kardeşim ! “ - Anonim

Bu bölümde istisnalar üzerinde duracağız. İstisna deyince aklınıza ne geliyor? Yanlış yazılmış uygulama mı ? Beklenmeyen durum mu ? Yoksa her ikiside mi ? İstisna demek işlerin sizin kontrolünüzden çıkması anlamına gelir, yani koas 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. 

Gerçekten tam bir uygulama yazmak ne demektir ? Uygulamadan beklenen görevleri yerine getirmesi onu tam bir uygulama kılar mı ? tabii ki kılmaz, 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 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ü B çok basit değil mi? . İlk etapda 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 her nedense ö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 ve 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ı tahmin edemilmiş 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. 

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 bunu kodu yazan kişiye, yazılan Java dosyasını derlemiyerek(compile) gerekli sert tavrı ortaya koyar. Java programlama dilinin bu tavrı doğrumudur ? 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 maksimimum şekilde yardımcı olmaktır, daha doğrusu insana dayalı oluşabilecek hataları kendi üstüne alarak, 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 dillerinde oluşturulan objelerin, daha sonradan işleri bitince hafızadan silinmemelerinden dolayı hafıza kaçakları oluşmaktadır. " Kodu yazan insan, oluşturduğu objeyi hafızadan temizlemez mi ? Ben bunu şahsen hiç yapmam, 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 hafıza canavarına dönüştürebilir ayrıca bu tür hafıza kaçaklarını uygulamanın içerisinden ayıklamak cidden çok zor bir iştir. Bu yüzden Java programlama dilinde, bir objenin hafızadan silinmesi kodu yazan kişiye göre değil, çöp toplama algoritmalarına göre yapılır (bkz). Javanın oluşabilecek olan hatalara karşı bu sert tutumu gayet mantıklıdır. Bu sert tutum ileride oluşabilecek ve bulunması çok güç olan hataların erkenden engellenmesine sağlar.

İstisna nasıl oluşabilir ?

İstisna oluşumuna en basit örnek olarak, yanlış kullanılmış dizi uygulamasını verebiliz. Java programlama dilinde dizilere erişim her zaman kontrollüdür. Bunun anlamı, Java programlama dilinin içerisinde dizilerin içerisine bir eleman atmak istiyorsak veya var olan eleman ulaşmak istiyorsak, bu işlemlerin hepsi Java programlama dili tarafından önce bir kontrolden geçirilir. Bunun bir avantajı, bir de dezavantajı vardır. Avantaj olarak güvenli bir dizi erişim mekanızması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 içerisinde güvenliktir. Aşağıdaki örneğimize dikkat edelim ;

ör-DiziErisim.java

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[] değişkeni ilkel (primitive) int tipinde dizi değişkenidir ve içerisinde 4 adet int tipinde eleman vardır. for döngüsünün sayesinde, sayilar[] dizi değişkeninin 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ı (run-time) hatasına sebebiyet verip, uygulamamızın çalışması aniden sonlanacaktır. Uygulamamızı çalıştırıp sonuçları hep beraber görelim. 

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, yani 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 yerleri belirlemek diğer dillere nazaran daha basittir. Örneğin bu uygulamızda oluşan istisnanın 10.  satırda ortaya çıktığını anlayabiliyoruz.

Başka istisnalar neler olabilir ? 

Bir uygulamanın başına gelebilecek istisnalar neler olabilir ? Bir kaç örnek verirsek ; 

  • Açmak istediğiniz fiziksel dosya yerinde olmayabilir

  • Kullanmak istediğiniz sınıf yerinde olmayabilir 

  • Network bağlantısı kopmuş olabilir

  • Yazmak istediğiniz dosya, başkası tarafından açılmış olduğundan yazma hakkınız olmayabilir

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

İ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; 

gösterim-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
}

İstisna çıkartması muhtemel kod, try bloğunun içerisinde tutularak güvenlik altına alınmış olur. Eğer istisna oluşursa, istisna yakalama mekanizması devereye girer ve  oluşan bu istinanın tipine göre, uygulamanın akışı catch bloklarından birinin içerisine yönlenerek devam eder.

İstisnalar objedir. Bir istisna oluştuğu zaman bir çok olay gerçekleşir. İlk önce yeni bir istisna objesi hafızanın heap alında new() anahtar kelimesi ile oluşturulur. Oluşan bu istisna objesinin 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 metodu cağıran metoda istisna objesi paslanır, eğer bu metod içerisinde de istisnayı yakalamak için catch bloğu tanımlanmamış ise istina objesi bir üst metoda paslanır, bu olay böyle devam eder ve en sonunda main() metoduna ulaşan istisana objesi için bir catch bloğu aranır eğer bu metodun içerisinde de catch bloğu tanımlanmamış ise, uygulananın akışı sonlanır. Bu olayları detaylı incelemeden evvel temel bir giriş yapalım;

ör-DiziErisim2.java

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 uygulama örneğimizde, 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 yakala makanizması sayesinde eğer istisna oluşursa bile uygulamanın akışı aniden sonlanmıyacaktır. DiziErisim.java  ile  DiziErisim2.java uygulamalarının çıktısına bakarsanız aradaki kontrolü hemen fark edeceksiniz. DiziErisim2.java uygulama örneğimizin çıktısı aşağıdaki gibidir.

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 kontol 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.

Yukarıdaki örneğimizde, try-catch mekanizmasını for döngüsünün içerisine koyabileceğiniz gibi, for döngüsünün kapsayacak şekilde de tasarlayıp yerleştirebilirsiniz.

ör-DiziErisim3.java

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. 

İstisna İfadeleri

Bir metod hangi tür istisna fırlatabileceğini önceden belirtebilir veya belirtmek zorunda kalabilir. Bu metodu çağıran diğer metodlar da, fırlatılabilecek olan bu istisnayı ya yakalarlar veya bir üst bölüme iletirler, bu böyle ancak main() metoduna kadar sürüp gidebilir. Bir üst bölümden kasıt edilen, bir metodu çağıran metod dur. Şimdi bir metodun neden önceden hangi tür istisna fırlatması gerektiğini inceliyelim.

ör-IstisnaOrnek1.java

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 anlaşılması çok güç olmasa gerek. 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ıştırsak, derleyicinin bize vereceği mesaj aşağıdaki gibi olur.

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

Derleyicinin kızdığı noktalar şunlardır; 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. 

İlk akla gelen 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 ? 

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

Aşağıdaki örneğimizde, oluşan istisnayı aynı metodun içerisinde yakalıyorum bu yüzden metodun hangi istisnayı fırlatabileceğini açıklamasına gerek yoktur. Bir metodun 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 inceliyelim 

ör-IstisnaOrnek2.java


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() metodu");
    }

    public static void main(String args[]) {
        IstisnaOrnek2 io2 = new IstisnaOrnek2();
        io2.calis();
        System.out.println("main() metodu");
    }
}

Yukarıdaki örneğimizde, dosyaya erişirken veya ondan birşeyler okumak isterken oluşabilecek olan istisnaları java.io.IOException istisna tipini kullanarak yakalayabiliriz. Zaten IstisnaOrnek.java uygumalasının derlemeye çalışırken alınan hatadan hangi tür istisna objesinin kullanılması gerektiğini de çıkartabilirtik (bkz) . java.io.FileNotFoundException istina tipini, java.io.IOException tipi kullanılarak yakalanabilir bunun nasıl olduğunu biraz sonra göreceğiz.

Yukarıdaki uygulamamız 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 ) zaman ilk olarak main() metodundan akışa başlanır . Daha sonra  calis() metodunun ve  cokCalis() metodunun çağrılması şeklinde akış devam ederken olanlar olur ve cokCalis() metodunun içerisinde istisna oluşur çünkü ornek.txt diye bir dosya ortalarda yoktur ama olsun içimiz rahat çünkü try-catch hata yakalama mekanizmamız mevcuttur. Anlattıklarımızı akış  diyagramında incelersek.....

 

Şekil-1

Akış şemasında numaralandılmış olan okları takip ederseniz olayların gelişimini çok rahat bir şekilde kavrayabilirsiniz. Akış diyagramımızı açıklamaya başlayalım;

  1. Öncelikle akış, main() metodunun içerisinden başlar. Bu uygulamamızda main() metodunun içerisinden calis() metodu çağrılmıştır.

  2. calis() metodunun içerisinden cokCalis() metodu çağrılmıştır.

  3. cokCalis() metodunun içerisinde istisna oluşmuştur çünkü uygulamamızın yer aldığı dizinin içerisinde  ornek.txt dosyası aranmış ve bulunamamıştır. Şimdi kritik an geldi, cokCalis() metodunun içerisinde try-catch mekanizması var mı ?

  4. Evet, cokCalis() metodunun içerisinde try-catch mekanizması olduğu için, catch bloğuna yazılmış olan kodlar çalışır. Bu uygulamamızda ekrana " Hata Yakalandi =java.io.FileNotFoundException: ornek.txt (The system cannot find the file specified) " basılır, yani dosyanın olmayışından dolayı bir istisna olduğu belirtilir. Not :  java.io.IOException istisna tipi,  java.io.FileNotFoundException istisna tipini kapsadığından bir sorun yaşanmaz bunun nasıl olduğunu biraz sonra inceliyeceğiz.

  5. Bitti mi ? tabii ki hayır, uygulamamız kaldığı yerden devam edecektir. Şimdi sıra calis() metodunun içerisindeki henüz çalıştırılmamış olan kodların  çalıştırılmasına. Burada da ekrana "calis() metodu" basılır.

  6. Son olarak akış main() metoduna geri döner ve main() metodunun içerisinde çalıştırılmamış olan kodlar çalıştırılır ve ekrana "main() metodu" basılır.

  7. Ve uygulamamız normal bir şekilde sona erer.

Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.

Hata Yakalandi =java.io.FileNotFoundException: ornek.txt (The system cannot find
the file specified)
calis() metodu
main() metodu

Akıllara şöyle bir soru gelebilir, "Eğer ornek.txt dosyası gerçekten olsaydı yine de  try-catch mekanizmasını yerleştirmek zorundamıydık" . Cevap evet , az önce bahseldiği gibi ortada istisna oluşma tehlikesi varsa bile bu tehlikenin önlemi Java programla dilinde önceden kesin olarak alınmalıdır.

IstisnaOrnek2.java  uygulamamızda, oluşan istisna aynı metodun içerisinde yakalanmıştır ve böylece uygulamanın akışı normal bir şekilde devam etmiştir. Peki oluşan bu istisnayı aynı metodun içerisinde yakalamamak gibi bir lüksümüz olabilir mi ? Yani oluşan istisna objesini -ki bu örneğimizde oluşan istisna objemizin tipi java.io.IOException idi, bir üst kısma fırlatılabilir mi ? Bir üst kısma fırlatmaktan kasıt edilen istisnanın meydana geldiği metodu çağıran metoda bu istisna objesini fırlatmaktır. "Peki ama niye böyle bişeye ihtiyaç duyalım ki" diyebilirsiniz. Bunun başlıca sebebi, istisnanın oluştuğu metod içerisinde, o istisna objesi ile ne yapacağımızı bilemiyebiliriz. Bir üst kısımda elimizde daha fazla bilgi olabilir, bu istisna objesini güzel bir şekilde değerlendirip, uygulamanın akışını ona göre yönlendirebiliriz.

ör-IstisnaOrnek3.java


import java.io.*;

public class IstisnaOrnek3 {

    public void cokCalis() throws IOException{

        File f = new File("ornek.txt");
        BufferedReader bf = new BufferedReader( new FileReader( f ) );
        System.out.println(bf.readLine());

    }

    public void calis() {
        try {  
            cokCalis();
            System.out.println("calis() metodu");
        } catch(IOException ex) {
            System.out.println("Hata Yakalandi-calis() =" + ex);
        }

    }

    public static void main(String args[]) {
        IstisnaOrnek3 io3 = new IstisnaOrnek3();
        io3.calis();
        System.out.println("main() metodu");
    }
}

IstisnaOrnek3.java örneğimizde, oluşan istisna, oluştuğu metod içerisinde yakalanmamıştır. Peki nasıl olurda derleyici buna kızmaz, cevabı hemen aşağıdadır.

gösterim-2

public void cokCalis() throws IOException {
     //..
}

Eğer bir istisna oluşursa, istisnanın oluştuğu metodun yapacağı iki şey vardır. Birincisi oluşan istisnayı kendi içerisinde try-catch mekanizmasıyla yakalıyabilir. İkincisi ise oluşacak olan istisnayı bir üst bölüme (kendisini çağıran metoda) fırlatabilir. Örneğin cokCalis() metodu "throws IOException" diyerek, kendisini çağıran metodlara şöyle bir mesaj gönderir, "Bakın benim içimde istisnaya yol açabilecek kod var ve eğer istisna oluşursa ben bunu fırlatıyorum, bu yüzden başınız çaresine bakın". Buraya kadar anlattıklarımızı akış diyagramında incelersek.....

 Şekil-2

Akış şemasında numaralandılmış olan okları takip ederseniz olayların gelişimini çok rahat bir şekilde kavrayabilirsiniz. Akış diyagramımızı açıklamaya başlayalım;

  1. Öncelikle akış, main() metodunun içerisinden başlar. Bu uygulamamızda main() metodunun içerisinden calis() metodu çağrılmıştır.

  2. calis() metodunun içerisinden cokCalis() metodu çağrılmıştır.

  3. cokCalis() metodunun içerisinde istisna oluşmuştur çünkü uygulamamızın yer aldığı dizinin içerisinde  ornek.txt dosyası aranmış ve bulunamamıştır. Şimdi kritik an geldi, cokCalis() metodunun içerisinde try-catch mekanizması var mı ?

  4. Hayır, cokCalis() metodunun içerisinde oluşan istisnayı yakalama mekanizması yoktur(try-catch) ama java.io.IOException tipinde bir hata objesi fırlatacağını "throws IOException" diyerek belirtmiştir. İstisna oluşmuş ve istisna objesi ( java.io.IOException ) bir üst bölüme yani calis() metoduna fırlatılmıştır.

  5. Artık istisna objemiz calis() metodunun içerisindedir, şimdi sorulması gereken soru " calis() metodunun içerisinde hata yakalama mekanizması var mıdır ? "

  6. calis() metodunun içerisinde hata yakalama mekanizması vardır (try-catch) bu yüzden catch bloğunun içerisindeki kod çalıştırılır ve ekrana " Hata Yakalandi-calis() =java.io.FileNotFoundException: ornek.txt (The system can not find the file specified) " basılır, yani dosyanın olmayışından dolayı bir istisna olduğu belirtilir. Dikkat edilirse ekrana " calis() metodu " basılmadı bunun sebebi istisnanın oluşmasından dolayı akışın catch bloğuna dallanmasıdır.  Not :  java.io.IOException istisna tipi,  java.io.FileNotFoundException istisna tipini kapsadığından bir sorun yaşanmaz bunun nasıl olduğunu biraz sonra inceliyeceğiz.

  7. Son olarak akış main() metoduna geri döner ve main() metodunun içerisinde çalıştırılmamış olan kodlar çalıştırılır ve ekrana "main() metodu" basılır.

  8. Ve uygulamamız normal bir şekilde sona erer.

Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.

Hata Yakalandi-calis() =java.io.FileNotFoundException: ornek.txt (The system can
not find the file specified)
main() metodu

Bu örneğimizdeki ana fikir, bir istisna kesin olarak oluştuğu metodun içerisinde yakalanmıyabileceğidir. Fırlatma özelliği sayesinde istisna objesi (eğer istisna oluşmuş ise) bir üst bölüme yani istisna oluşan metodu çağıran metoda fırlatılabilir.

Peki bu istisna objesi (java.io.IOException) calis() metodun yakalanmasaydı ne olurdu ? Cevap: O zaman main() metodun yakalanırdı. Nasıl ? Hemen gösterelim.

ör-IstisnaOrnek4.java

import java.io.*;

public class IstisnaOrnek4 {

    public void cokCalis() throws IOException {
        File f = new File("ornek.txt");
        BufferedReader bf = new BufferedReader( new FileReader( f ) );
        System.out.println(bf.readLine());   
    }

    public void calis() throws IOException {
        cokCalis();
        System.out.println("calis() metodu");
    }

    public static void main(String args[]) {
        try { 
            IstisnaOrnek4 io4 = new IstisnaOrnek4();
            io4.calis();
            System.out.println("main() metodu");
        } catch(IOException ex) {
            System.out.println("Hata Yakalandi-main() =" + ex);
        }
    }
}

Bu sefer biraz daha abartıp, oluşan istisna objesini son anda main() metodunda yakalıyoruz. Bu örneğimizde hem istisnanın meydana geldiği cokCalis() metodu hemde calis() metodu oluşan istisnayı fırlatmışlardır. Olaylara bu açıdan bakarsak, oluşan akış sanki çoğunuzun bildiği "papaz kaçtı"  kağıt oyununu anımsatmaktadır. Buradaki papaz kağıdı ise oluşan istisna objesidir. Buraya kadar anlattıklarımızı akış diyagramında incelersek.....

Şekil-3

Akış şemasında numaralandılmış olan okları takip ederseniz olayların gelişimini çok rahat bir şekilde kavrayabilirsiniz. Akış diyagramımızı açıklamaya başlayalım;

  1. Öncelikle akış, main() metodunun içerisinden başlar. Bu uygulamamızda main() metodunun içerisinden calis() metodu çağrılmıştır.

  2. calis() metodunun içerisinden cokCalis() metodu çağrılmıştır.

  3. cokCalis() metodunun içerisinde istisna oluşmuştur çünkü uygulamamızın yer aldığı dizinin içerisinde  ornek.txt dosyası aranmış ve bulunamamıştır. Şimdi kritik an geldi, cokCalis() metodunun içerisinde try-catch mekanizması var mı ?

  4. cokCalis() metodunun içerisinde oluşan istisnayı yakalama mekanizması yoktur(try-catch) ama java.io.IOException tipinde bir hata objesi fırlatacağını "throws IOException" diyerek belirtmiştir. İstisna oluşmuş ve istisna objesi ( java.io.IOException ) bir üst bölüme yani calis() metoduna fırlatılmıştır.

  5. Artık istisna objemiz calis() metodunun içerisindedir, şimdi sorulması gereken soru " calis() metodunun içerisinde hata yakalama mekanizması var mıdır ? "

  6. Cevap hayırdır. calis() metodu da oluşan istisna objesini bir üst bölüme yani kendisini çağıran main() metoduna fırlatmıştır.

  7. İstina objemiz main() metodunun içerisine geldi. Sorulması gereken soru " main() metodunun içerisinde hata yakalama mekanizması var mıdır ? "

  8. Cevap evettir. Böylece akış main() metodunun içerisindeki catch bloğuna dallanır ve catch bloğunun içerisindeki kod çalıştırılır.

  9. Ve uygulamamız normal bir şekilde sona erer.

Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.

Hata Yakalandi-main() =java.io.FileNotFoundException: ornek.txt (The system cann
ot find the file specified)

Oluşan bir istisna objesini catch  bloğunda yakalamanın ne gibi avantajları olabilir ? Bu sorunun cevabına değinmeden evvel olaylara eğer istisna objesi main() metodunda yakalanmasaydı neler olucağını inceleyerek başlıyalım.

ör-IstisnaOrnek5.java

import java.io.*;

public class IstisnaOrnek5 {

    public void cokCalis() throws IOException {
        File f = new File("ornek.txt");
        BufferedReader bf = new BufferedReader( new FileReader( f ) );
        System.out.println(bf.readLine());   
    }

    public void calis() throws IOException {
        cokCalis();
        System.out.println("calis() metodu");
    }

    public static void main(String args[]) throws IOException { 
        IstisnaOrnek5 io5 = new IstisnaOrnek5();
        io5.calis();
        System.out.println("main() metodu");
    }
}

Görüldüğü üzere cokCalis() metodunun içerisinde oluşan istisna hiçbir metod içerisinde hata yakalama mekanizması kullanılarak yakalanmamıştır(try-catch). Bunun yerine tüm metodlar bu istisna objesini fırlatmayı seçmiştir, buna main() metoduda dahildir. Böyle bir durumda akışın nasıl gerçekleştiğini, akış diyagramında inceliyelim.....

Şekil-4

Akış şemasında numaralandılmış olan okları takip ederseniz olayların gelişimini çok rahat bir şekilde kavrayabilirsiniz. Akış diyagramımızı açıklamaya başlayalım;

  1. Öncelikle akış, main() metodunun içerisinden başlar. Bu uygulamamızda main() metodunun içerisinden calis() metodu çağrılmıştır.

  2. calis() metodunun içerisinden cokCalis() metodu çağrılmıştır.

  3. cokCalis() metodunun içerisinde istisna oluşmuştur çünkü uygulamamızın yer aldığı dizinin içerisinde  ornek.txt dosyası aranmış ve bulunamamıştır. Şimdi kritik an geldi, cokCalis() metodunun içerisinde try-catch mekanizması var mı ?

  4. cokCalis() metodunun içerisinde oluşan istisnayı yakalama mekanizması yoktur(try-catch) ama java.io.IOException tipinde bir hata objesi fırlatacağını "throws IOException" diyerek belirtmiştir. İstisna oluşmuş ve istisna objesi ( java.io.IOException ) bir üst bölüme yani calis() metoduna fırlatılmıştır.

  5. Artık istisna objemiz calis() metodunun içerisindedir, şimdi sorulması gereken soru " calis() metodunun içerisinde hata yakalama mekanizması var mıdır ? "

  6. Cevap hayırdır. calis() metodu da oluşan istisna objesini bir üst bölüme yani kendisini çağıran main() metoduna fırlatmıştır.

  7. İstina objemiz main() metodunun içerisine geldi. Sorulması gereken soru " main metodunun içerisinde hata yakalama mekanizması var mıdır ? "

  8. Cevap hayırdır. hımm peki ne olacak ? Çok basit, uygulama doğal olarak sonlanacaktır.

Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.

 

Exception in thread "main" java.io.FileNotFoundException: ornek.txt (The system
cannot find the file specified)
        at java.io.FileInputStream.open(Native Method)
        at java.io.FileInputStream.<init>(FileInputStream.java:103)
        at java.io.FileReader.<init>(FileReader.java:51)
        at IstisnaOrnek5.cokCalis(IstisnaOrnek5.java:8)
        at IstisnaOrnek5.calis(IstisnaOrnek5.java:13)
        at IstisnaOrnek5.main(IstisnaOrnek5.java:19)

"Hata yakalama mekanizması koyduğumuzda da uygulama sonlanıyordu, şimdide sonlandı bunda ne var ki" diyebilirsiniz. Haklı olabilirsiniz ama önce oluşan bir istisna objesini catch  bloğunda yakalamanın ne gibi avantajları olabilir ? sorusunun cevaplarını verelim .

Oluşan bir istisna objesini catch bloğundan yakalamak, daha doğrusu hata yakalama mekanizması kullanmak uygulamayı yazan kişilere büyük kolaylıklar sağlar. En büyük avantaj oluşan hatayı catch bloğunun içerisinde loglayabilirsiniz (kaydebilrsiniz ama dosyaya ama veri tabanına.. gibi gibi..)  . Örneğin iyi işleyen bir uygulama yazdınız vebu uygulama yaptığınız  tüm -daha doğrusu aklınıza gelen- tüm testlerden geçmiş herşey harika, kendinize güveniniz gelmiş, dünya gözünüze artık bambaşka bir yer gibi geliyor  ama bir gün bir bakıyorsunuz ki uygulamanız çalışması durmuş !! ilk yapacağınız şey "bu uygulamayı kim kapattı !" diye etrafa sormak oysaki kimsenin günahı yok, kimse elini uygulamanıza sürmemiştir zaten böyle bir riski kim alabilir ki ? Asıl gerçek uygulamada ters giden bisey olmuş ve uygulama kapanmıştır. İşte tam o anda tutunacağınız tek dal dosyaya veya veri tabanına kayıt ettiğiniz hata mesajlarıdır , yani loglarınızdır. Bu bakımdan catch bloğunun içerisine oluşan hata ile ne kadar detaylı bilgiler gömmerseniz bu bilgiler sizi ileride -eğer hata oluşursa-  o kadar yardımcı olacaktır.

IstisnaOrnek5.java kötü bir uygulama örneğidir. Oluşabilecek olan bir istisna kesin olarak hata yakalama mekanizması(try-catch) ile sizin öngördüğümüz bir yerlede yakalanmalıdır. Bir istisna meydana geldiği zaman uygulama kesin olarak sonlanmak zorunda değildir. Eğer bir telafisi var ise bu catch bloğunun içerisinde yapılmalı ve uygulama tekrardan ayağa kaldırılmalıdır ama çok ölümcül bir hata ise o zaman hata mesajını loglamaktan (kaydebilrsiniz ama dosyaya ama veri tabanına.. gibi gibi..)  başka yapılacak pek fazla bişey yoktur.

İstisna tip hiyerarşisi

Nasıl olurda java.io.IOException istisna tipi,  java.io.FileNotFoundException istisna tipini kapsayabilir ? Kapsamak ne demektir ? Kapsamak demek, eğer uygulamanızda java.io.FileNotFoundException tipinde bir istisna objesi oluşmuşsa (bir istisna oluşmuşsa) bu istisna tipini java.io.IOException tipini kullanarak catch bloğunda  yakalabileceğiniz anlamına gelir. 

Şekil-5

Yukardaki şemamızdan görüleceği üzere, FileNotFoundException istisna tipi, IOException in alt kümesi  olduğu için, FileNotFoundException tipinde bir istisna objesini catch bloğunun içerisinde IOException istisna tipiyle yakalıyabiliriz.

Throwable istisna objesi, tüm istisna objelerinin atasıdır. Yukarıdaki şemamızıa bakarak istisnaları 3 alt kümeye bölebiliriz.

  • Error istisna tipi ölümcül bir hatayı işarettir ve telafisi çok zordur, neredeyse imkansızdır. Örneğin OutOfMemoryError istisna tipi, hafızanın dolayı bir istisna meydana gelmiş ise, uygulamanın buna müdahele edip düzeltmesi imkansızdır.

  • RuntimeException istisna tipleri, eğer uygulama normal seyrinde giderse ortaya çıkmaması gereken  istisna tipleridir. Örneğin ArrayIndexOutOfBoundsException istisna tipi, bir dizinin olmayan elamanına eriştiğimiz zaman ortaya çıkan bir istisnadır. RuntimeException istisna tipleri, yanlış kodlamadan veya tasarımdan dolayı meydana gelen istisna tipleri diyebiliz. Biraz sonra bu istisna tipini  detaylı biçimde inceliyeceğiz.

  • Ve diğer Exception tipleri. Bu istisna tipleri çevresel koşullardan dolayı meydana gelebilir. Örneğin erişmeye çalışan dosyanın yerinde olmaması (FileNotFoundException) veya network bağlantısının kopması sonucu ortaya çıkabilecek olan istisnalardır ve bu istisnalar için önceden tedbir alınması şarttır.

Tüm diğer Exception istisna tiplerini yakalamak

Bir uygulama içerisinde oluşabilecek olan tüm diğer Exception tiplerini yakalamak için aşağıdaki ifadeyi kullanabilirsiniz.

gösterim-3

catch (Exception ex) {
    // ......
}

Tüm istisnaları yakalamak (Error, RuntimeException ve diğer Exception türleri) için Throwable istisna tipini kullanmak iyi fikir değilDİR. Bunun yerine bu  üç kümeye ait daha özellikli istisna tiplerinin kullanılmasını öneriyorum.

RuntimeException istisna tipleri 

DiziErisim.java uygulama örneğimiz içerisinde istisna oluşma riski olmasına rağmen nasıl oldu da java buna kızmayarak derledi ? Peki ama IstisnaOrnek1.java uygulamasını niye derlemedi ? Bu soruların cevapları istisna tiplerinin iyi bilenmesi ile ortaya çıkar.

DiziErisim.java uygulama örneğinde istisna oluşma riski vardı , eğer uygulamayı yazan kişi dizinin olmayan bir elemanına erişmeye kalkarsa ArrayIndexOutOfBoundsException hatası alacaktır, yani RuntimeException(çalışma-anı hatası). Peki bunun sebebi nedir ? Bunun sebebi kodu yazan arkadaşın dikkatsizce davranmasıdır. Bu tür hataların derleme anında (compile-time) farkedilemez ve Java bu tür hatalar için bir tedbir alınmasını şart koşmaz ama yinede tedbir almakta özgürsünüzdür (bir dosyaya erişirken Java bunun için tedbir alınmasını şart koşar çünkü bu tür hatalar diğer Exception istisna tipine girer) . Genel olarak karşılaşılan RuntimeException istisna türlerine bir bakalım ;

  • AritmeticException : Bir sayının sıfıra bölünmesiye ortaya çıkabilecek olan RuntimeException istisna tipi .

gösterim-4

 int i = 16 / 0 ; // AritmeticException ! hata !
  • NullPointerException : Şahsen benim çokca karşılaştığım bir istisna tipi, sebebi ise tamamen dikatsizlik. Bir sınıf tipindeki değişkeni, o sınıfa ait bir objeye bağlamadan kullanmaya kalkınca alınabilecek bir istisna tipi.

gösterim-5

String ad ;
System.out.println("Ad = " + ad.trim() ); // NullPointerException ! hata !

Bu hatayı almamak için ;

gösterim-6

String ad = " Java Kitap Projesi  "; // baglama islemi
System.out.println("Ad = " + ad.trim() ); //dogru
  • NegativeArraySizeException : Bir diziyi negatif bir sayı vererek oluşturmaya çalışırsak bu istisna tipi ile karşılaşırız.

gösterim-7

 int dizi[] = new dizi[ -100 ];  // NegativeArraySizeException  ! hata !
  • ArrayIndexOutOfBoundsException : Bir dizinin olmayan elemanına ulaşmak istendiği zaman karşılaşılan istisna tipi, daha detaylı bilgi için DiziErisim.java uygulama örneğini inceleyiniz.

 

  • SecurityException : Genellikle tarayıcı (browser) tarafından fırlatılan bir istisna tipidir. Bu istisnaya neden olabilecek olan sebebler aşağıdaki gibidir ;

    • Applet içerisinden, local bir dosyaya erişilmek istetendiği zaman.

    • Appletin indirildiği servera(sunucuya) değildi değişik bir server a network bağlantısı kurulmaya çalışıldığı zaman.

    • Applet in kendi içerisinde başka bir uygulama başlatılmaya çalıştığı zaman.

    SecurityException istisna tipi fırlatılır.

Önemli noktayı bir kez daha vurgulayalım, RuntimeException ve bu istisna tipine ait alt tipleri yakalamak için Java bizlere bir zorlama yapmaz.

İstisna Mesajları

Bir istisna objesinden bir çok veri elde edebilirsiniz. Örneğin istisna oluşumunun yol haritasını izleyebilirsiniz veya istisna oluşana kadar hangi metodlar çağrılmış gibi gibi değerli bilgiler. 

Ana istisna tipi olan Throwable sınıfına ait olan getMessage(), getLocalizedMessage() ve toString() metodlarının ne iş yaptıklarını örnek uygulama üzerinde inceliyelim.

ör-IstisnaMetodlari.java


public class IstisnaMetodlari {


    public void oku() throws Exception {
        throw new Exception("istisna firlatildi"); // dikkat
    }


    public static void main(String args[]) {
        try {
           IstisnaMetodlari im = new IstisnaMetodlari();
           im.oku();
        } catch (Exception ex) {
             System.out.println("Hata- ex.getMessage() : " + ex.getMessage() );
             System.out.println("Hata-ex.getLocalizedMessage() : " + 
                                                    ex.getLocalizedMessage() );             
             System.out.println("Hata- ex.toString() : " + ex );  
        }
    }
}

oku() metodunun içerisinden bilinçli olarak Exception istisna objeye oluşturulup fırlatılmıştır. (Exception sınıfı Throwable sınıfından türediği için, Throwable sınıfı içerisindeki public ve protected olan değişkenler ve metodlar otomatik olarak Exception sınıfının içerisinde de bulunur) Bu istisna sınıfının yapılandırıcısına ise kısa bir not düştüm. main() metodunun içerisindeki catch bloğunda Exception istisna sınıfına ait metodlar kullanılarak, oluşan istina hakkında daha fazla bilgi alınabilir. Bu metodların detaylı açıklamasını aşağıdaki gibidir.  

  • String  getMessage() : Oluşan istisnaya ait bilgileri String tipinde geri döner.  Bu örneğimizde bilgi olarak " istisna firlatildi " yazdik bu sebebten dolayı bu metod bize bu bilgiyi String tipinde geri döndürecektir. Eğer biz Exception sınıfının yapılandırıcısına bişey göndermeseydik o zaman bu metod bize null dönerdi.

  • String getLocalizedMessage() : Bu metod, Exception sınıfından türetilmiş alt sınıflar tarafından iptal edilebilir (override). Biraz sonra kendi istisna sınıflarımızı nasıl oluşturacağımızı gördüğümüzde bu metod daha bir anlam taşıyacaktır. Eğer bu metod alt sınıflar tarafından iptal edilmemiş ise  getMassage() metodunu ile aynı sonucu döndürür.  

  • String toString() : Oluşan istisna hakkında kısa bir açıklamayı String tipinde geri döner. Eğer istisna sınıfına ait objeyi bir açıklama ile oluşturmuşsak - new Exception("hata fırlatıldı") gibi -  bunu da ekrana basar. toString() metodu oluşan istisna ile ilgili bilgiyi belli bir kural içerisinde ekrana basar. 

    • Oluşan istisna objesinin tipini ekrana basar

    • ": "  iki nokta üst üste koyar ve bir boşluk bırakır.

    • Son olarak getMassege() metodu çağrılır ve buradan - eğer bilgi varsa" ekrana basılır.

    Eğer oluşan istisna sınıfına ait obje bir açıklama ile oluşturulmamış ise yani direk - new Exception() - diyerek oluşturulmuş  ise son adımda "null" yerine hiçbirsey basmaz.

Uygulamamızın çıktısı aşağıdaki gibi olur.

Hata- ex.getMessage() : istisna firlatildi
Hata-ex.getLocalizedMessage() : istisna firlatildi
Hata- ex.toString() : java.lang.Exception: istisna firlatildi

Java 1.4 ile gelen Throwable sınıfına ait bir başka metod ise getCause() dur. Bu metod Throwable tipinde bir istisna objesi geri döner. Buradaki amaç oluşmuş olan istisnanın -eğer varsa- sebebini daha detaylı bir biçimde yakalamaktır.

ör-IstisnaMetodlari2.java

import java.io.*;

public class IstisnaMetodlari2 {


    public void oku() throws Exception {
        throw new Exception("istisna firlatildi",new IOException() ); // dikkat
    }


    public static void main(String args[]) {
        try {
           IstisnaMetodlari2 im2 = new IstisnaMetodlari2();
           im2.oku();
        } catch (Exception ex) {
             System.out.println("Hata- ex.getCause() : " + ex.getCause() );  
        }
    }
}

Bu örneğimizde java.io.IOException  kullanıldığı için import java.io.* demek zorundaydım. Bu kısa açıklamadan sonra detayları vermeye başlayalım. 

  • Throwable getCause() : Bu metodun işe yaraması için,  istisna sınıfına ait yapılandırıcının içerisine bu istisnaya sebebiyet vermiş olan istisna tipini yerleştirmemiz gerekmektedir. Tekrardan belirtelim bu metod Throwable tipinde bir obje geri döner.

gösterim-8

throw new Exception("istisna firlatildi",new IOException()); // dikkat

Böyle bir ifade nerede işimize yararki diyebilirsiniz. Bu olay aslında aynı anda iki tip istisna fırlatabilmenize olanak tanır ve bu çoğu yerde işinize yarıyabilir. Eğer istisnanın oluştuğu yerde alt istisna objesini belirtilmemiş ise - throw new Exception("istisna firlatildi")  gibi-  getCause() metodunu "null" dönecektir.

Uygulamamızın çıktısı aşağıdaki gibi olur.

Hata- ex.getCause() : java.io.IOException

Java 1.4 ile gelen bir başka yenilik ise initCause() metodudur. Bu metod iki istisna tipini birleştirmeye yarar. 

ör-IstisnaMetodlari3.java

import java.io.*;

public class IstisnaMetodlari3 {


    public void oku() throws Throwable {
        Exception ioEx  =  new IOException(); // dikkat
        Exception fnfEx =  new FileNotFoundException(); // dikkat
        Throwable th = ioEx.initCause(fnfEx);
        throw th;

    }


    public static void main(String args[]) {
        try {
           IstisnaMetodlari3 im3 = new IstisnaMetodlari3();
           im3.oku();
        } catch (Throwable th) {
            // Throwable th = ex.getCause();  
            //Throwable th2 = ex.initCause(th); // hata
            System.out.println("Hata - th.initCause() : " + th );  
            System.out.println("Hata - th.getCause() : " + th.getCause() );  

        }
    }
}

oku() metodunun içerisinde iki ayrı tipte istisna objesi oluşturulmuştur. Bu istisna objelerini birleştirip tek bir Throwable tipinde obje oluşturmak mümkündür. Hatanın yakalandığı yerde birleştirilen bu iki istisna tipine ayrı ayrı ulaşılabilir.

  • Throwable initCause( Throwable cause) : İki ayrı istisna tipini birleştirmeye yarar.  Eğer bir istisna Throwable(Throwable) veya Throwable(String, Throwable) ile oluşturulmuş ise initCause() metodu çağrılamaz.

Uygulamamızın çıktısı aşağıdaki gibi olur.

Hata - th.initCause() : java.io.IOException
Hata - th.getCause() : java.io.FileNotFoundException

Oluşan bir hatanın yol haritasını printStackTrace() metodu sayesinde görebilirsiniz. 

ör-IstisnaMetodlari4.java

public class IstisnaMetodlari4 {
    
    public void cokOku() throws Exception {
        System.out.println("cokOku() metodu cagrildi");
        throw new Exception("istisna olustu"); // dikkat
    }

    public void oku() throws Exception {
        System.out.println("oku() metodu cagrildi");
        cokOku();
    }


    public static void main(String args[]) {
        try {
           IstisnaMetodlari4 im4 = new IstisnaMetodlari4();
           im4.oku();
        } catch (Exception ex) {
             ex.printStackTrace(); 
        }
    }
}
  • printStackTrace() : Throwable sınıfının bu metodu sayesinde oluşan bir istisnanın yol haritasını görebiliriz. Yol haritası demek örneğin bir istisna oluşmuş ise bunun hangi satırda meydana geldiği, istisnanın oluştuğu metodu hangi metod çağırmış gibi soruların cevaplarının bulunduğu bir çeşit bilgi kümesi diyebiliriz. printStackTrace() metodu hatayı System.err kullanarak kullanıcıya iletir. Bunun ne gibi avantajları var derseniz hemen açıklıyalım: Eğer bir uygulamanın çıktısını dosyaya veya buna benzer bir yere yönlendirilmiş ise System.out kullanarak yazılmış ifadeler yine bu dosyalara ve buna benzer yerlere yazılacaktır ama System.err kullanılarak yazılmış bir ifade, uygulama nereye yönledirilmiş olursa olsun  kesin olarak konsola yazılır ve kullanıcının dikkatine sunulur.

  • printStackTrace( PrintStream s ) : PrintStream sınıfına ait obje kullanılarak, oluşan istisnanın yol haritasını konsol yerine başka bir yere bastırmanız mümkündür.  Başka bir yer derken, örneğin  bir dosya veya network bağlantısı ile başka bir bilgisayara  oluşan bu istisnanın yol haritasını gönderebilirsiniz.

  • printStackTrace( PrintWriter s ) : PrintWriter sınıfına ait obje kullanılarak, oluşan istisnanın yol haritasını konsol yerine başka bir yere bastırmanız mümkündür. Özellikle JSP ve Servlet kullanırken oluşan bir istisnanın yol haritasını HTTP/HTTPS  kanalı ile kullanıcılara gösterilebilir.

Uygulamamızın çıktısı aşağıdaki gibi olur.

oku() metodu cagrildi
cokOku() metodu cagrildi
java.lang.Exception: istisna olustu
        at IstisnaMetodlari4.cokOku(IstisnaMetodlari4.java:10)
        at IstisnaMetodlari4.oku(IstisnaMetodlari4.java:15)
        at IstisnaMetodlari4.main(IstisnaMetodlari4.java:22)

Oluşan bir istisnanın yol haritasını Throwable objesi şeklinde elde etmeniz fillInStackTrace() metodunu sayesinde mümkündür. Bu olay istisnanın tekrardan fırlatılması söz konusu olduğunda - biraz sonra inceliyeceğiz -  faydalı olabilir. 

ör-IstisnaMetodlari5.java

public class IstisnaMetodlari5 {

    

    public void cokOku() throws Exception {
        System.out.println("cokOku() metodu cagrildi");
        throw new Exception("istisna olustu");
    }

    public void oku() throws Exception {
        System.out.println("oku() metodu cagrildi");
        cokOku();
    }

    public static void main(String args[]) {
       try {
          IstisnaMetodlari5 im5 = new IstisnaMetodlari5();
          im5.oku();
       } catch (Exception ex) {
            Throwable t = ex.fillInStackTrace();
            System.err.println( t.getMessage() );
       }  
    }
}
  • Throwable fillInStackTrace() : Oluşan istisnanın yol haritasına müdahele ederek değiştirir ve değiştirilen bilgiler ışığında yeni bir  Throwable objesi  geri döndürür.

Uygulamamızın çıktısı aşağıdaki gibi olur.

oku() metodu cagrildi
cokOku() metodu cagrildi
istisna olustu

Yine Java 1.4 ile gelen getStackTrace() metodu,  printStackTrace() metodu ile oluşan hata satırlarını StackTraceElement dizisi şeklinde geri döner.

ör-IstisnaMetodlari6.java

public class IstisnaMetodlari6 { 
    
    public void cokOku() throws Exception {
        System.out.println("cokOku() metodu cagrildi");
        throw new Exception("istisna olustu");
    }

    public void oku() throws Exception {
        System.out.println("oku() metodu cagrildi");
        cokOku();
    }

    public static void main(String args[]) {
        try {
           IstisnaMetodlari6 im6 = new IstisnaMetodlari6();
           im6.oku();
        } catch (Exception ex) {
             StackTraceElement[] ste = ex.getStackTrace(); // dikkat
             for(int i=0 ;i < ste.length;i++) {
                System.err.println("--> " + ste[i].getFileName() +" - "+
                                            ste[i].getMethodName() +" - "+
                                            ste[i].getLineNumber() );
             }  
        }
    }
}
  • StackTraceElement[]  getStackTrace() : Programatik olarak oluşan istisnanın yol haritası bilgilerine ulaşımını bu metod sayesinde yapabilirsiniz. Bu metod oluşan istisnaya ait yol bilgilerini bir StackTraceElement dizisi şeklinde sunar.  printStackTrace() metodunun çıktısı göz önüne getirirsek, buradaki ilk satır, StackTraceElement dizisinin ilk elemanına denk gelir. Bu dizinin son elemanıda oluşan istisna yol haritasının son satırına denk gelir.

Uygulamamızın çıktısı aşağıdaki gibi olur.

oku() metodu cagrildi
cokOku() metodu cagrildi
--> IstisnaMetodlari6.java - cokOku - 8
--> IstisnaMetodlari6.java - oku - 13
--> IstisnaMetodlari6.java - main - 20

Son olarak inceliyeceğimiz metod yine Java 1.4 ile birlikte gelen setStackTrace() metodudur. Bu metod sayesinde oluşan istisnanın yol haritasını değiştirebilirsiniz.

ör-IstisnaMetodlari6.java

public class IstisnaMetodlari7 { 
    
    public void cokOku() throws Exception {
        System.out.println("cokOku() metodu cagrildi");
        Exception eE = new Exception("istisna olustu-1"); // dikkat
        System.out.println("------------");
        Exception eE2 = new Exception("olusan istisna-2"); // dikkat
        eE2.setStackTrace( eE.getStackTrace() ); // dikkat
        throw eE2; // dikkat

    }

    public void oku() throws Exception {
        System.out.println("oku() metodu cagrildi");
        cokOku();
    }


    public static void main(String args[]) {
        try {
           IstisnaMetodlari7 im7 = new IstisnaMetodlari7();
           im7.oku();
        } catch (Exception ex) {                          
             StackTraceElement[] ste = ex.getStackTrace(); // dikkat
             for(int i=0 ;i < ste.length;i++) {
                System.err.println("--> " + ste[i].getFileName() +" - "+
                                            ste[i].getMethodName()+" - "+
                                            ste[i].getLineNumber() );
             }  
        }
    }
}
  • setStackTrace( StackTraceElement[]  stackTrace) : Bu metodu kullanarak oluşan istnanın yol haritasını değiştirebiliriz. Bu değişimden sonra getStackTrace() veya printStackTrace() gibi benzeri metodların sonuçlarında da artık değişen bu yeni yol haritasını basacaklardır.

Uygulamamızın çıktısı aşağıdaki gibi olur.

oku() metodu cagrildi
cokOku() metodu cagrildi
------------
--> istisnametodlari7.java - cokOku - 6
--> istisnametodlari7.java - oku - 16
--> istisnametodlari7.java - main - 23

Yukarıdaki örneğimizde fırlatılan istisnanın çıkış noktası 9. satırda olmasına rağmen, setStackTrace() metodu kullanığımız için oluşan istisnanın yol haritası üzerinde değişiklik oluşmuştur.

Kendi istisnalarımızı nasıl oluşturabiliriz ? 

Javanın kendi içerisinde tanımlanmış istisna tiplerinin dışında, bizlerde kendimize özgü istisna tiplerini oluşturup kullanabiliriz. Sonuçta istisnalar da birer objedir,  kendilerine has durumları ve özellikleri olabilir. İlk istisna sınıfımızı oluşturalım;

ör-BenimHatam.java

public class BenimHatam extends Exception {
    
    private int id ;

    public BenimHatam() {
    }

    public BenimHatam(String aciklama) {
        super(aciklama); // dikkat
    }

    public BenimHatam(String aciklama , int id) {
        super(aciklama); //dikkat
        this.id = id ;
    }
    
    public String getLocalizedMessage() {  // iptal etme (override)
          
        switch(id) {
            case 0 : return "onemsiz hatacik" ;
            case 1 : return "hata" ;
            case 2 : return "! onemli hata !" ; 
            default: return "tanimsiz hata";
        }
        
    }

    public int getId() {
        return id;
    }
}

İlk istisna sınıfımızın ismi BenimHatam. Şimdi olaylara geniş açıdan bakıldığında görülmesi gereken ilk şey,  bir sınıfın istisna tipleri arasında yer alabilmesi için Exception sınıfından türetilmesi gerektiğidir.

gösterim-9

public class BenimHatam extends Exception {
    //..
}

Exception sınıfından türetilen bir sınıf artık istisna sınıfları arasında yerini almaya hazırdır fakat önce bir kaç önemli noktayı  gün ışığına çıkartalım. Öncelikle BenimHatam istisna sınıfının yapılandırıcılarına (constructor) dikkat etmenizi istiyorum. BenimHatam istisna sınıfının iki adet yapılandıcısı (constructor) bulunmaktadır, bunlardan biri String tipinde diğeri ise bir String birde ilkel int tipinde parametre kabul etmektedir. Bu yapılandırıcıların ortak olarak aldıkları parametre tipi String tipidir. Niye ?  

BenimHatam istisna sınıfından anlaşılacağı üzere, bu sınıfımızın içerisinde aynı diğer sınıflarımızda oldu gibi metodlar tanımlayabildik, örneğin getId() metodu. Bu metod hataya ait Id numarasını dönmektedir, hangi hata numarası, bu da ne ? demeyin çünkü bu metodları zenginlik katması için kafadan salladım .

Bu sınıfımızın içerisinde Throwable  ( Exception sınıfının da Thowable sınıfından türetildiğini unutmayalım ) sınıfının bir metodu olan getLocalizedMessage() metodunu iptal etmiş (override) bulunmaktayız. Yukarıdaki açıklamalardan hatırlayacağınız üzere eğer getLocalizedMessage() metodu iptal edilmez (override) ise getMessage() ile aynı açıklamayı dönerdi (bkz). Fakat biz burada sırf heyacan olsun diye her numarayı bir açıklama ile eşleştirip geri döndürmekteyiz. Örneğin  "sıfır = önemsiz hata", "bir =  ! önemli hata !" gibi gibi, tabii bunlarda tamamen hayal ürünü olarak yazılmıştır. 

İstisna sınıflarının yapılandıcılarına String ifade göndermenin amacı getMessage() metodunun yaptığı işi anlamaktan geçer. Tabii sadece istisna sınıfının yapılandırıcısına String tipde parametre göndermek ile iş bitmez. Bu gönderilen parametre eğer super(String) komutunun çağrılsa amaçına ulaşır. Peki amaç nedir ? Diyelim ki bir dosya açmak istediniz ama açılmak istenen dosya yerinde değil ? Böyle bir durumda java.io.FileNotFoundException tipinde bir istisna objesi oluşturulacaktır. Oluşan istisna ile (dosyanin bulunamaması) oluşan istisna tipinin ismi (java.io.FileNotFoundException) arasında bir ilişki kurabiliyormusunuz ?  "FileNotFoundException" ifadesinin türkçesi "dosya bulunamadı istisnası" demektir, bingo ! . Böyle bir durumda oluşan istisnayı, bu istisna için oluşturulan istisna objesinin ismine bakarak aklımızda bir ışık yanabilir. Peki her istisna tipinin ismi bu kadar açıklayıcı mıdır?  Örneğin SQLException, bu ifadenin türkçesi "SQL istisnası" demektir. Böyle bir istisna bazı zamanlarda anlamsız gelebilir. Evet SQL istisnası çok güzel  ama niye ? Ne ters gitti de ben bu hatayı aldım, ek açıklama yok mu diyeceğimiz anlar olmuştur ve olacaktır. Sonuçta ek açıklamalara ihtiyaç olduğu bir gerçektir. İşte bu ek açıklamaları bu istisna sınıflarının yapılandırıcılarına gönderebiliriz böylece oluşan istisna objesinin ismi ve bizim vereceğimiz ek açıklamalar ile oluşan sır perdesini aralanabilir. Ek açıklamanın String tipinde olmasına herhalde kimsenin bir itirazı yoktur. İşte bu yüzden iyi tasarlanmış bir istisna sınıfının String tipinde parametre kabul eden yapılandırıcıları vardır. Ama ek olarak yukarıdaki örneğimizde olduğu gibi hem String hemde ilkel (primitive) int tipinde parametre kabul eden yapılandırıcılar olabilir. Fazla parametre göz çıkartmaz .

Şimdi ikinci istisna sınıfımız olan SeninHatan sınıfını inceliyelim;

ör-SeninHatan.java

public class SeninHatan extends Exception { 

    public SeninHatan() {
    }

    public SeninHatan(String aciklama) {
        super(aciklama); // dikkat
    }
}

O kadar anlat anlat sonra gel parametresiz bir yapılandırcı (constructor) oluştur ! Yukarıda anlattıklarımızın uygulamasını yapmadan olur mu ? olmaz tabii ki . Bu sınıfımız bir öncekine göre daha sadedir. Şimde tek eksiğimiz bu istisna sınıflarımızın kullanıldığı bir kobay örnek. Onu da hemen yazalım. 

ör-Kobay.java

public class Kobay {


    public void cikart(int a , int b) throws BenimHatam, SeninHatan {
         if(a == 0) {
             throw new SeninHatan("a parametresi sifir geldi");
         }
         if(b == 0) {
             throw new SeninHatan("b parametresi sifir geldi");
         }
         if( (a<0) || (b<0) ) {
            throw new SeninHatan(); // kotu, aciklama yok
         }

         int sonuc = a - b ; // hesaplama islemi
         
         if(sonuc < 0) {
            throw new BenimHatam("sonuc eksi",2);
         }else if( sonuc == 0) {
            throw new BenimHatam("sonuc sifir",1);
         }
    }

    public static void main(String args[]) {
        System.out.println("----------------------"); 
        try {
            Kobay it = new Kobay();
            it.cikart(1,2);
         } catch (BenimHatam ex1) {
             System.out.println( "Hata Olustu-1:"+ ex1.getMessage() );
             System.out.println(ex1.getLocalizedMessage());
             System.out.println(ex1.getId()); 
         } catch (SeninHatan ex2) {
             System.out.println("Hata Olustu-2:"+ ex2);
         }

         System.out.println("----------------------");
         try {
            Kobay it = new Kobay();
            it.cikart(1,0);
         } catch (BenimHatam ex1) {
             System.out.println("Hata Olustu-1:"+ ex1.getMessage());
             System.out.println(ex1.getLocalizedMessage());
             System.out.println(ex1.getId());
         } catch (SeninHatan ex2) {
             System.out.println("Hata Olustu-2:"+ ex2);
         }

         System.out.println("----------------------");
         try {
            Kobay it = new Kobay();
            it.cikart(1,-124); 
         } catch (BenimHatam ex1) {
             System.out.println("Hata Olustu-1:"+ ex1.getMessage());
             System.out.println(ex1.getLocalizedMessage());
             System.out.println(ex1.getId());
         } catch (SeninHatan ex2) {
             System.out.println("Hata Olustu-2:"+ ex2);
         }
    }    
}

Yukarıdaki örneğimizde üç adet harekete kızılmaktadır. Bunlar sırasıyla :

  • Sonucun eksi çıkması durumunda BenimHatam tipinde istisna oluşmakta

  • Parametrelerden birinin sıfır gönderilmesi durumunda SeninHatan tipinde istisna oluşmakta

  • Parametrelerden birinin eksi gönderilmesi durumunda SeninHatan tipinde istisna oluşmakta

Eğer BenimHatam tipinde bir istisna oluşursa nasıl detaylı bilgi alınacağına dikkat lütfen dikkat edin. Aynı şekilde SeninHatan tipinde bir istisna oluşursa ekrana sadece toString()  metodundan geri dönen açıklama gönderilecektir. SeninHatan istisnasının fırlatıldığı yerlere dikkat ederseniz, ek açıklamaların ne kadar hayati bir önem taşıdığını göreceksiniz. Uygulamanın çıktısı aşağıdaki gibidir.

----------------------
Hata Olustu-1:sonuc eksi
! onemli hata !
2
----------------------
Hata Olustu-2:SeninHatan: b parametresi sifir geldi
----------------------
Hata Olustu-2:SeninHatan

SeninHatan istisna tipinin nasıl meydana geldiğini gönderilen ek açıklama ile daha iyi kavrayabiliyoruz ama son try-catch bloğunda yakalanan SeninHatan istisna tipinin sebebi açık değildir. Ortada bir istisna vardır ama bu istisnayı nasıl giderebileceğimiz konusunda bilgi yoktur. Bu uygulama karmaşık olmadığı için kolaylıkla " burada oluşan isitsnanın sebebi parametrenin eksi gönderilmesidir " diyebilirsiniz ama çok daha büyük uygulamalarda bu tür çıkarımlar yapmak zannedildiği kadar kolay olmayabilir.

Not : ( Java 1.4 sürümünün JavaDoc larına göre Throwable sınıfına ait toString() metodunun getMessage() metodundan geri dönen bilgilere dikkate aldığı söylensede, aslında getLocalizedMessage() metodunu dikkat almaktadır. Bu hata 25.05.2002 tarihi itibariyle düzeltilme aşamasında imiş bugId : 4298805 , isteyenler http://java.sun.com adresinden gelişmeleri takip edebilirler.) 

finally bloğu 

Bir işlemin her koşulda - istisna olsun ya da olmasın - kesin olarak yapılmasını istiyorsak finally bloğu kullanmalıyız.

gösterim-10

try {
    // riskli kod 
    // bu kod BenimHatam,SeninHatan
    // OnunHatasi, BizimHatamiz 
    //  tipinde istisnalar firlatabilir

} catch(BenimHatam bh) {
    //  BenimHatam olusursa buraya 
} catch(SeninHatan sh) {
    //  SeninHatan olusursa buraya 
} catch(OnunHatasi oh) {
    //  OnunHatasi olusursa buraya
} catch(BizimHatamiz bizh) {
    //  BizimHatamiz olusursa buraya
} finally {
    // ne olursa olsun calisacak kod buraya
}

Ne olursa olsun calışmasını istediğiniz kodu finally bloğuna yazabilirsiniz. Bir uygulama üzerinde açıklamaya çalışırsak .

ör-FinallyOrnek1.java

public class FinallyOrnek1 {

    public static void a(int deger) throws  SeninHatan {
        if(deger < 0 ) {
            throw new SeninHatan();
        }
    }

    public void hesapla() {

        for(int i=-1 ; i < 1 ; i++ ) {
            try { 
                System.out.println("a() cagriliyor"); 
                a(i); 
            } catch(SeninHatan shEx) {
                System.out.println("SeninHatan olustu : " + shEx);
            } finally {
                System.out.println("finally blogu calistirildi");
            }
        }
    }

    public static void main(String args[]) {
        FinallyOrnek1 fo1 = new FinallyOrnek1();
        fo1.hesapla();
    }
}

Bu uygulamamızda dikkat edilmesi gereken yer hesapla() metodudur. for döngüsü -1 den 0 'a kadar ilerlemektedir, ilerleyen bu değerler a() metoduna parametre olarak gönderilmektedir. a() metodunun içerisinde ise gelen parametrenin değeri kontrol edilip eğer bu değer 0 dan küçükse SeniHatan istisnası fırlatılmaktadır. Buradaki amaç bir istisnalı birde istisnasız koşulu yakalıyarak her koşulda finally bloğuna girildiğini ispatlamaktır. Uygulamanın çıktısı aşağıdaki gibidir.

a() cagriliyor
SeninHatan olustu : SeninHatan
finally blogu calistirildi 
a() cagriliyor
finally blogu calistirildi

Ayrıca finally bloğunun daha bir çok faydası bulunur. Örneğin birşey aramak için geceleğin bir odaya girdiğinizde ilk olarak ne yaparsanız ? Genelleme yaparak ışığı yakarsanız diyelim. Aynı şekilde odanın içerisinde arama işlemi bittiğinde ve odaya terk edeceğiniz zaman ne yaparsanız ? Açık olan ışığı kapattırsınız değil mi ? Harika , sonuçta odadan çıkarken ışığın kapatılması gerekir bunun her zaman olması gereken bir davranış olarak kabul edip uygulamamızı inceliyelim. 

ör-Oda.java


class BeklenmeyenHata1 extends Exception {

    public  BeklenmeyenHata1(String ekAciklama) {
        super(ekAciklama);
    }
}

class BeklenmeyenHata2 extends Exception {

    public  BeklenmeyenHata2(String ekAciklama) {
        super(ekAciklama);
    }
}

public class Oda {

    public void isiklariKapat() {
        System.out.println("isiklar kapatildi");
    }

    public void isiklariAc() {
        System.out.println("isiklar acildi");
    }

    public void aramaYap() throws BeklenmeyenHata1, BeklenmeyenHata2 {
          // istisna firlatabilecek olan govde
    }

    public void basla() {
        try {
            // riskli kod
            isiklariAc();
            aramaYap();
            isiklariKapat(); // dikkat
        } catch(BeklenmeyenHata1 bh1)  {
            System.out.println("BeklenmeyenHata1 yakalandi");
            isiklariKapat(); // dikkat

        } catch(BeklenmeyenHata2 bh2)  {
            System.out.println("isiklar acildi");
            isiklariKapat(); // dikkat
        }
    }

    public static void main(String args[]) {
        Oda o = new Oda();
        o.basla();
    }
}

Bu örneğimizde basla() metodunda gelişen olaylara dikkat edelim. Karanlık bir odada arama yapmak için ilk önce ışıkları açıyoruz daha sonra aramayı gerçekleştiriyoruz ve en sonunda ışıkları kapatıyoruz. Fakat dikkat edin ışıkların kapanmasını garantilemek için üç ayrı yerde isiklariKapat()  metodu çağrılmaktadır, peki ama niye ? 

try bloğunun içerisine yerleştirilen isiklariKapat() metodu, eğer herşey yolunda giderse çağrılacaktır. BeklenmeyenHata1 istisnasının yakalandığı catch bloğundaki isiklariKapat() metodu, eğer BeklenmeyenHata1 istisnası oluşursa ışıkların kapatılması unutulmasın diye yerleştirilmiştir. Aynı şekilde BeklenmeyenHata2 istisnasının yakalandığı catch bloğundaki isiklariKapat() metodu, eğer BeklenmeyenHata2 istisnası oluşursa ışıkların kapatılması garantilemek amaçı için buraya  yerleştirilmiştir. Bir işi yapabilmek için aynı kodu üç farklı yere yazmak ne kadar verimlidir. Daha karmaşık bir yapıda belkide ışıkları söndürmeyi unutulabilir. İşte böyle bir durumda finally bloğu hem verimliliği artırmak hemde çalışması istenen kodu çalışmasını garantilemek amacıyla kullanılabilir. Yukarıdaki uygulama örneğimizin değiştirilmiş versiyonunu tekrardan yazarsak . 

ör-Oda2.java

public class Oda2 {

    public void isiklariKapat() {
        System.out.println("isiklar kapatildi");
    }

    public void isiklariAc() {
        System.out.println("isiklar acildi");
    }

    public void aramaYap() throws BeklenmeyenHata1, BeklenmeyenHata2 {
          // istisna firlatabilecek olan govde
    }

    public void basla() {
        try {
            // riskli kod
            isiklariAc();
            aramaYap();
        } catch(BeklenmeyenHata1 bh1)  {
            System.out.println("BeklenmeyenHata1 yakalandi");
        } catch(BeklenmeyenHata2 bh2)  {
            System.out.println("isiklar acildi");
        } finally {
            isiklariKapat(); // dikkat
        }
    }

    public static void main(String args[]) {
        Oda2 o2 = new Oda2();
        o.basla();
    }
}

Bu uygulama örneğimizde isiklariKapat() metodu sadece finally bloğunun içerisine yazılarak her zaman - her koşulda çalıştırılması garanti hale getirilmiştir. Artık herhangi bir istisna oluşsun veya oluşmasın ışıklar kesin olarak söndürülecektir.

finally bloğunun kesin olarak çağrıldığını aşağıdaki uygulamamızdan da görebiliriz.

ör-FinallyOrnek2.java

public class FinallyOrnek2 {


    public static void main(String args[]) {

        try {
            System.out.println("1-  try blogu");
            try {
                System.out.println("2-  try blogu");
                throw new Exception();
            } finally {
                System.out.println("2-  finally blogu");
            }  

        } catch(Exception ex) {
            System.out.println("1-  catch blogu");
        } finally {
            System.out.println("1-  finally blogu");
        }
    }
}

Bu örneğimizde iç tarafdaki try bloğunun içerisinde bir istisna oluşturulmuştur. İç tarafdaki try bloğunun catch mekanizması olmadığı için bu oluşan istisna dış tarafdaki catch mekanizması tarafından yakalanacaktır. Fakat bu yakalanma işleminin hemen öncesinde iç kısımda bulunan finally bloğunun içerisindeki kodlar çalıştırılacaktır. Uygulamanın çıktısı aşağıdaki gibidir.

1- try blogu
2- try blogu
2- finally blogu
1- catch blogu
1- finally blogu
return ve finally bloğu

finally bloğu her zaman çalıştırılır. Örneğin bir metod hiçbirşey döndürmüyorsa - void - ama bu metodun içerisinde, metodu sesizce terk etmek amacı ile return ifadesi kullanılmış ise, finally bloğu içerisindeki kodlar bu return ifadesi devreye girmeden hemen öncesinde çağrılır. Uygulama üzerinde göstermeye çalışırsak.

ör-ReturnOrnek.java

public class ReturnOrnek { 

    public void calis(int deger) {
        try {
            System.out.println("calis metodu cagrildi, gelen deger : " + deger);
            if(deger == 0) {
                return ; // metodu sessizce terk et
            }
            System.out.println("-- calis metodu normal bir sekilde bitti--");
        } catch (Exception ex) {
            System.out.println("catch blogu icerisinde");      
        } finally {
            System.out.println("finally blogu cagrildi");
            System.out.println("----------------------");

        }
    }

    public static void main(String args[]) {
          ReturnOrnek ro = new ReturnOrnek();
          ro.calis(1);
          ro.calis(0); // dikkat
    }
}

calis() metoduna gönderilen parametre eğer sıfırsa, bu metod çalışmasını sona erdiriyor fakat finally bloğu içerisindeki kodlar bu durumda bile çalıştırılmaktadır.  Dikkat edilmesi gereken bir başka nokta ise calis() metodunun bilerek bişey döndürmemesidir - void -  olmasıdır. Çünkü eğer calis() metodu birşey - ör: String tipi- döndüreceğini söyleseydi, bu ifadeye kesin olarak finally bloğunun içerisinde yapması gerekirdi aksi takdirde derleme anında (compile-time) uyarı alırdık , yani try bloğunun içerisinde return ile bir değer geri döndürülmesi izin verilmez eğer ki o metodun içerisinde try - finally blok sistemi tanımlanmış ise. Uygulamanın çıktısı aşağıdaki gibidir.

calis metodu cagrildi, gelen deger : 1
-- calis metodu normal bir sekilde bitti--
finally blogu cagrildi
----------------------
calis metodu cagrildi, gelen deger : 0
finally blogu cagrildi
----------------------
Dikkat System.exit();

Eğer System sınıfının statik bir metodu olan exit()  çağrılırsa finally bloğuna hiç girilmez. System.exit() metodu uygulamanın içerisinde çalıştığı JVM'i (Java virtual machine)  kapatır. Anlatılanları bir uygulama üzerinde incelersek. 

ör-SystemExitOrnek.java


public class SystemExitOrnek { 

    public void calis(int deger) {
        try {
            System.out.println("calis metodu cagrildi, gelen deger : " + deger);
            if(deger == 0) {
                System.exit(-1) ; // JVM'i kapat
            }
            System.out.println("-- calis metodu normal bir sekilde bitti--");
        } catch (Exception ex) {
            System.out.println("catch blogu icerisinde");      
        } finally {
            System.out.println("finally blogu cagrildi");
            System.out.println("----------------------");

        }
    }

    public static void main(String args[]) {
          SystemExitOrnek seo = new SystemExitOrnek();
          seo.calis(1);
          seo.calis(0); // dikkat
    }
}

Bu örneğimizin bir öncekine göre tek farkı return yerine System.exit() komutunun yazılmış olmasıdır. System.exit() komutu, uygulamanın içerisinde çalıştığı JVM'i kapattır. exit() metoduna gönderilen eksi bir değer JVM'in anormal bir sonlanış yapacağını ifade eder. Bu çok ağır bir cezalandırmadır. Normalde uygulamanın bu şekilde sonlandırılması pek tercih edilmemektedir ancak tek başına çalışan (standalone) uygulamalarda kullanıcının yanlış parametre girmesi sonucu kullanılanılabilir. Tomcat (JSP ve Servlet container) gibi bir çok insanın paylaştığı sunucularda  System.exit() komutunun kullanılması Tomcat sunucusunun çalışmasını sonlandıracaktır -ki bu istenmeyen bir durumdur. 

İstisnanın tekrardan fırlatılması

Oluşan bir istisnayı catch bloğunda yakalandıktan sonra tekrardan bir üst kısma fırlatmanız mümkündür. Genel gösterim aşağıdaki gibidir . 

gösterim-11

try {
    // riskli kod
} catch  (Exception ex){
    System.out.println("istisna yakalandi : " + ex);
    throw ex ; // dikkat
}

Oluşan bir istisnayı bir üst kısma fırlatırken istisna objesinin içerisindeki bilgiler saklı kalır. Bir uygulama üzerinde incelersek .

ör-TekrarFirlatimOrnek1.java


public class TekrarFirlatimOrnek1 {


    public void cokCalis() throws Exception {
        try {
            throw new Exception("oylesine bir istisna"); // istisnanin olusumu
        } catch(Exception ex) {
            System.out.println("cokCalis() istisna yakalandi : " + ex);
            throw ex ; // dikkat 
        }
    } 


    public void calis() throws Exception {
        try {
            cokCalis();
        } catch(Exception ex) {
            System.out.println("calis() istisna yakalandi : " + ex);
            throw ex ; // dikkat 

        }
    }


    public void basla() {
        try {
            calis();
        } catch(Exception ex) {
            ex.printStackTrace(); // bilgi alimi
        }

    }

    public static void main(String args[]) {
        TekrarFirlatimOrnek1 tfo1 = new TekrarFirlatimOrnek1();
        tfo1.basla();
    }

}

Yukarıdaki örneğimizde istisna cokCalis() metodunun içerisinde oluşmaktadır. Oluşan bu istisna catch mekanizması sayesinde yakalandıktan sonta tekrardan bir üst kısma fırlatılmaktadır. calis() metodunun içeriside de aynı şekilde fırlatılan istisna catch mekanizması sayesinde yakalanıp tekrardan bir üst kısma fırlatılmaktadır. basla() metoduna kadar gelen istisna objesi burada yakalanıp içerisinde saklı bulunan bilgiler printStackTrace() metoduyla gün ışığına çıkarılmaktadır. Uygulamanın çıktısı aşağıdaki gibidir.

cokCalis() istisna yakalandi : java.lang.Exception: oylesine bir istisna
calis() istisna yakalandi : java.lang.Exception: oylesine bir istisna
java.lang.Exception: oylesine bir istisna
        at TekrarFirlatimOrnek1.cokCalis(TekrarFirlatimOrnek1.java:7)
        at TekrarFirlatimOrnek1.calis(TekrarFirlatimOrnek1.java:17)
        at TekrarFirlatimOrnek1.basla(TekrarFirlatimOrnek1.java:29)
        at TekrarFirlatimOrnek1.main(TekrarFirlatimOrnek1.java:38)

Dikkat ederseniz oluşan istisnaya ait bilgiler basla() metodunun içerisinde ekrana basılmasına karşın, orijinalliğini hiç kaybetmedi. Orijinallikten kasıt edilen istisnanın gerçekten nerede oluştuğunu yukarıdaki çıktıya bakarak anlayabilmemizdir.

Oluşan bir istisnayı yakalayıp yeniden fırlatmadan evvel, onun içerisindeki bilgilere müdahale etmeniz mümkündür. Şöyle ki ... 

ör-TekrarFirlatimOrnek2.java

public class TekrarFirlatimOrnek2 {


    public void cokCalis() throws Exception {
        try {
            throw new Exception("oylesine bir istisna");
        } catch(Exception ex) {
            System.out.println("cokCalis() istisna yakalandi : " + ex);
            throw ex ; // dikkat 
        }
    } 


    public void calis() throws Throwable {
        try {
            cokCalis();
        } catch(Exception ex) {
            System.out.println("calis() istisna yakalandi : " + ex);
            throw ex.fillInStackTrace() ; // dikkat 

        } 
    }


    public void basla() {
        try {
            calis();
        } catch(Throwable th) {
            th.printStackTrace(); // dokum
        } 
    }

    public static void main(String args[]) {
        TekrarFirlatimOrnek2 tfo2 = new TekrarFirlatimOrnek2();
        tfo2.basla();
    }  
}

Bu örneğimizde istisnanın orijinal oluşma yeri cokCalis() metodudur ama calis() metodu içerisinde istisna objesinin içindeki bilgilere fillInStackTrace() müdahale edilip değiştirilmektedir. Uygulamanın çıktısı aşağıdaki gibidir.

cokCalis() istisna yakalandi : java.lang.Exception: oylesine bir istisna
calis() istisna yakalandi : java.lang.Exception: oylesine bir istisna
java.lang.Exception: oylesine bir istisna
        at TekrarFirlatimOrnek2.calis(TekrarFirlatimOrnek2.java:20)
        at TekrarFirlatimOrnek2.basla(TekrarFirlatimOrnek2.java:28)
        at TekrarFirlatimOrnek2.main(TekrarFirlatimOrnek2.java:36)

Artık istisnanın oluşma yeri olarak 20. satırı yani fillInStackTrace() metodunun devreye girdiği yeri göstermektedir. Böylece oluşan istisnanın içerisindeki bilgilere müdahale etmiş bulunmaktayız. calis() metodunun niye Throwable tipinde bir istisna fırlatıldığına gelince, bunun sebebi fillInStackTrace() metodunun Throwable tipinde bir istisna objesi geri döndürmesidir. Bu sebebten dolayı basla() metodunun içerisindeki catch bloğunda Exception istisna tipi yerine Throwable tipi belirtilmiştir. Eğer bu catch bloğunda Exception tipi belirtilseydi derleme anında (compile-time) Throwable yakalanmalı diye hata alınırdı. Şekil 5'e dikkat ederseniz Throwable istisna tipi en üste bulunmaktadır. Bunun anlamı eğer bir Throwable tipinde istisna fırlatılmış ise bunu kesin olarak catch bloğunun içerisinde Throwable tipi belirterek yakalıyabileceğimizdir , daha aşağısıda bir tip kurtarmıyacaktır.

ör-Rutbe.java

public class Rutbe {

    public static void main(String args[]) {
        try { 
            throw new Throwable();
        } catch ( Exception ex ) {
            System.out.println(" istisna yakalandi : " + ex);
        }        
    }  
}

Yukarıdaki örneğimizi derlemeye (compile) çalıştığımız zaman aşağıdaki hata mesajı ile karşılaşırız.

Rutbe.java:7: unreported exception java.lang.Throwable; must be caught or declar
ed to be thrown
            throw new Throwable();
            ^
1 error

Bunun anlamı, Throwable tipindeki bir objeyi catch bloğunun içerisinde Exception tipi belirtilerek yakalıyamıyacağızdır.

printStackTrace() ve hata mesajlarının kısaltılması

Java 1.4 ile beraber gelen bir başka özellik ise Throwable sınıfının yapılandırıcısına bir başka istisna tipideki objeyi parametre olarak gönderebiliyor olmamızdır.  Bu özellikten daha evvel bahsetmiştik, esas ilginç olan bu özelliğin fazla kullanılmasıyla aynı hata mesajlarının tekrarlamasıdır. Java tekrarlayan bu hata mesajları için bir kısaltma kullanır.

ör-Kisaltma.java


class YuksekSeviyeliIstisna extends Exception {
	YuksekSeviyeliIstisna(Throwable cause) {
		super(cause);
	}
}

class OrtaSeviyeliIstisna extends Exception {
	OrtaSeviyeliIstisna(Throwable cause) {
		super(cause);
	}
}

class DusukSeviyeliIstisna extends Exception {
}

public class Kisaltma {
	public static void main(String args[]) {
		try {
			a();
		} catch(YuksekSeviyeliIstisna e) {
			e.printStackTrace();
		}
	}
	static void a() throws YuksekSeviyeliIstisna {
		try {
			b();
		} catch(OrtaSeviyeliIstisna e) {
			throw new YuksekSeviyeliIstisna(e);
		}
	}
	static void b() throws OrtaSeviyeliIstisna {
		c();
	}   
	static void c() throws OrtaSeviyeliIstisna {
		try {
			d();
		} catch(DusukSeviyeliIstisna e) {
			throw new OrtaSeviyeliIstisna(e);
		}
	}
	static void d() throws DusukSeviyeliIstisna { 
		e();
	}
	static void e() throws DusukSeviyeliIstisna {
		throw new DusukSeviyeliIstisna(); // baslangic
	}
}

Yukarıdaki örneğimizde üç adet istisna tipi bulunmaktadır. e() metodunun içerisinde başlayan istisnalar zinciri main() metodunun içerisinde son bulmaktadır. Buradaki olay oluşan bir istisnayı diğerine ekleyerek aynı tip hata mesajları elde etmektir. Uygulamanın çıktısı aşağıdaki gibidir.

YuksekSeviyeliIstisna: OrtaSeviyeliIstisna: DusukSeviyeliIstisna
        at Kisaltma.a(Kisaltma.java:29)
        at Kisaltma.main(Kisaltma.java:20)
Caused by: OrtaSeviyeliIstisna: DusukSeviyeliIstisna
        at Kisaltma.c(Kisaltma.java:39)
        at Kisaltma.b(Kisaltma.java:33)
        at Kisaltma.a(Kisaltma.java:27)
        ... 1 more
Caused by: DusukSeviyeliIstisna
        at Kisaltma.e(Kisaltma.java:46)
        at Kisaltma.d(Kisaltma.java:43)
        at Kisaltma.c(Kisaltma.java:37)
        ... 3 more

Uygulamanın çıktısından da anlaşılağı üzere, tekrar eden kısmın kaç kere tekrar ettiği bilgisi de verilmektedir. Mesela : 

at Kisaltma.c(Kisaltma.java:39)
at Kisaltma.b(Kisaltma.java:33)
at Kisaltma.a(Kisaltma.java:27)

Bu kısım 1 kere tekrar etmiştir aynı şekilde 

at Kisaltma.e(Kisaltma.java:46)
at Kisaltma.d(Kisaltma.java:43)
at Kisaltma.c(Kisaltma.java:37)

Bu kısım ise 3 kere tekrar etmiştir. 

İlginç gelişme

Oluşan bir istisna her zaman fırlatılamıyabilir. Aşağıdaki uygulamamızı inceliyelim

ör-FirlatimOrnek1.java

public class FirlatimOrnek1 {


	public void basla(int a, int b) throws Exception {
		int	 sonuc = 0 ;  
		try {
			sonuc = a / b ; 
		} catch(Exception ex) {
			System.out.println("basla() istisna yakalandi"); 
			throw ex; 
		} finally {
			System.out.println("sonuc : "+ sonuc); 
		}
	}

	public static void main(String args[]) {
		try {
			FirlatimOrnek1 fo1 = new FirlatimOrnek1();
			fo1.basla(1,0);
		} catch(Exception ex) {
			System.out.println("main() istisna yakalandi"); 
		}
	}
}

Yukarıdaki örneğimizde akışın nasıl olmasını bekleriz ? İlkel (primitive) int tipinde bir sayının sıfıra bölünmesi sonuçu ArithmeticException tipinde bir istisna oluşur aynı bizim bu örneğimizde olduğu gibi. Tamam istisna oluştu peki ya sonra ? Sonra ekrana finally içerisinde tanımlanmış ifade yazılır ve en son olarak istisna objesi bir üst kısma fırlatılır. Herşey beklendiği gibi gitmekte, harika ! Uygulamamızın çıktısı aşağıdaki gibidir.

basla() istisna yakalandi
sonuc : 0
main() istisna yakalandi

Önce basla() metodunun içerisinde yakalanan istisna, finally bloğunun çalıştırılmasından sonra  bir üst kısma fırlatalabilmiştir. Fırlatılan bu istisna main() metodu içerisinde yakalanmaktadır.

Peki ya basla() metodu bir değer döndürseydi olaylar nasıl değişirdi ? 

ör-FirlatimOrnek2.java

public class FirlatimOrnek2 {


	public int basla(int a, int b) throws Exception {
		int sonuc = 0 ;  
		try {
			sonuc = a / b ; 
		} catch(Exception ex) {
			System.out.println("basla() istisna yakalandi"); 
			throw ex; 
		} finally {
			System.out.println("sonuc : "+ sonuc); 
			return sonuc ; // dikkat
		}
	}

	public static void main(String args[]) {
		try {
			FirlatimOrnek2 fo2 = new FirlatimOrnek2();
			fo2.basla(1,0);
		} catch(Exception ex) {
			System.out.println("main() istisna yakalandi"); 
		}
	}
}

Uygulamamızın çıktısı nasıl olacaktır ? Bir önceki uygulama ile aynı mı ? Yoksa daha mı farklı ? Fazla heyecan yapmadan uygulamamızın çıktısı hemen verelim.

basla() istisna yakalandi
sonuc : 0

Oluşan istisna, basla() metodunda yakalanmıştır ama daha sonra ortalardan kaybolmuştur . Aslında bu olay hata gibi algılanabilir ve haklı bir algılamadır. Fakat olaylara birde Java tarafından bakarsak biraz anlayış gösterilebilir. Bir metodun bir seferde sadece tek bir şey döndürme hakkı vardır. Ya bir değer döndürebilir veya bir istisna fırlatabilir, sonuçta fırlatılan bir istisna da değer niteliği taşır. Bu uygulamamızda basla() metodu int tipinde değer döndüreceğini söylediği için, oluşan bir istisnanın tekrardan fılatılması olanaksızdır. Bu işin bir çözümü var mı ? Düşünelim. Bir metod bir değer döndürse bile eğer bir istisna oluşursa bu oluşan istisnayı öncelikli olarak nasıl fırlatabilir ? Böyle bir ikilem ile er ya da geç karşı karşıya kalınacaktır. Aşağıdaki gibi bir çözüm iş görebilir.

ör-FirlatimOrnek3.java

public class FirlatimOrnek3 {


	public int basla(int a, int b) throws Exception {
		int sonuc = 0 ;  
		Exception globalEx = null ;
		try {
			sonuc = a / b ; 
		} catch(Exception ex) {
			System.out.println("basla() istisna yakalandi"); 
			globalEx = ex ; // aktarim
		} finally {
			System.out.println("sonuc : "+ sonuc); 
			if(globalEx != null) { // eger istisna olusmus ise
			    throw globalEx ; // tekrardan firlatim
			}
			return sonuc ;  // degeri geri dondur
		}
	}

	public static void main(String args[]) {
		try {
			FirlatimOrnek3 fo3 = new FirlatimOrnek3();
			fo3.basla(1,1);
			fo3.basla(1,0);
		} catch(Exception ex) {
			System.out.println("main() istisna yakalandi"); 
		}
	}
}

Yukarıdaki örneğimizde, eğer bir istisna oluşmuş ise Exception tipinde tanımlanan globalEx değişkenine, catch bloğu içerisinde değer aktarılmaktadır. finally bloğunun içerisinde globalEx değişkenin bir istisna objesine bağlı olup olmadığı kontrol edilmektedir. Eğer globalEx null değerinden farklıysa, bu catch bloğunda bir istisna objesine bağlandığı anlamına gelir yani bir istisnanın oluştuğunu ifade eder. Eğer globalEx null değerine eşitse sorun yok demektir. Böylece istisna oluşmuş ise finally bloğunda istisna fırlatılır, değilse de metod normal dönmesi gereken değeri geri döndürür. Uygulamamızın çıktısı aşağıdaki gibidir.

sonuc : 1
basla() istisna yakalandi
sonuc : 0
main() istisna yakalandi

İptal etme (overrride) ve istisnalar

İptal etme (override) konusunu bölüm-5'de incelemiştik (bkz). Bir sınıftan türetilen bir alt sınıfın içerisinde, üst (ana) sınıfa ait bir metodu iptal edebilmesi için bir çok şart aranmaktaydı, bunlar sırasıyla, iptal eden metodun, iptal edilen metod ile aynı parametrelere, aynı isme ve üst sınıfa ait metodun erişim belirliyicisinden daha erişilebilir veya aynı erişim beliryicisine sahip olması gerekirdi. Bunları tekrardan hatırlamak için lütfen bölüm-5 bir daha göz atın. Buraya kadar anlattıklarımızda hem fikirsek esas soruyu sorabilirim, işte soru: İptal etme (override) ile istisnalar arasında bir bağlantı olabilir mi ? Bu konu için bir başlık ayrıldığına göre herhalde bir bağlıntı var ama nasıl ? Bir uygulama üzerinde incelersek.

ör-AB.java


import java.io.*;

class A {

    public void basla() throws FileNotFoundException, EOFException {
	// ...
    } 
}     

public class AB extends A  {
    public void basla() throws  IOException {
	//...
    }
}

AB.java uygulamasını derlemeye (compile) çalıştığımız zaman aşağıdaki hata mesajını alırız. 

AB.java:12: basla() in AB cannot override basla() in A; overridden method does n
ot throw java.io.IOException
    public void basla() throws  IOException {
                ^
1 error

Bu hata mesajının anlamı nedir ? AB sınıfının içerisindeki basla() metodunun, A sınıfnın içerisindeki basla() metodunu iptal edemediği çok açıktır, bunun sebebi erişim belirliyiciler olabilir mi ? Hayır olamaz çünkü hem iptal eden de edilen metod aynı erişim belirliyicisine sahip - public erişim belirliyicisine - Hımm peki sorun nerede ? Sorun istisna tiplerinde. A sınıfına ait basla() metodu iki adet istisna objesi fırlatıyor (FileNotFoundException ve EOFException) ama bunu iptal etmeye çalışan AB sınıfına ait basla() metodu sadece bir tane istisna objesi fırlatıyor (IOException), sorun bu olabilir mi ? 

İptal edememe sorununu anlamak için şekil-5 deki yapıyı incelemek gerekir. Bu şeklimizden görüleceği üzere FileNotFoundException ve EOFException istisna tipleri, IOException istisna tipinden türetilmişlerdir. Kötü haberi hemen verelim, iptal ederken (override) artık yeni bir kuralımız daha oldu, şöyle ki: iptal edilen  metodunun (AB sınıfının içerisindeki basla() metodu)  fırlatacağı istisna tipi , iptal eden metodun (A sınıfı içerisindeki basla() metodu) fırlatacağı istisna tiplerini kapsamalıdır. Aşağıdaki uygulamamız bu kuralı doğru bir şekilde yerine getirmektedir.

Ör-CD.java


import java.io.*;

class C {

    public void basla() throws IOException {
	// ...
    } 
}     

public class CD extends C  {
    public void basla() throws FileNotFoundException, EOFException {
	//...
    }
}

İşte doğru bir iptal etme (override) örneği.  C sınıfının basla() metodu -iptal edilen- sadece bir adet istisna objesi fırlatmaktadır (IOException) fakat CD sınıfının basla() metodu -iptal eden- iki adet istisna objesi fırlatmaktadır (FileNotFoundException ve EOFException ). Buradan çıkarılacak sonuç doğru bir iptal etme işlemi için fırlatılan istisna objelerinin sayısı değil tiplerinin önemli olduğudur. Şekil-5 bakmaktan usandık diyebilirsiniz ama son bir kez daha şekil-5 bakarsak, IOException istisna tipinin, FileNotFoundException ve EOFException  istisna tiplerini kapsadığını görürsünüz yani FileNotFoundException ve EOFException tipinde istisna tipleri fırlatılırsa bu istisnaları IOException tipi ile catch bloğunda yakalanabilir ama bunun tam tersi olanaksızdır. Daha ilginç bir örnek verelim.

ör-EF.java


import java.io.*;

class E {

    public void basla() throws  IOException {
	// ...
    } 
}     

public class EF extends E  {
    public void basla()  {
	//...
    }
}

İptal edilen metod IOException tipinde bir istisna objesi fırlatmasına karşın, iptal eden metodun hiç bir istisna objesi fırlatmama lüksü vardır. İptal eden metodun hiç bir istisna objesi fırlatmaması bir soruna yol açmaz. Peki ama niye ? Niye iptal edilen metodun daha kapsamlı bir istisna objesi fırlatması gerekir ? Bu sorunun cevabını daha kapsamlı bir örnek üzerinde inceliyelim.

ör-Sekreter.java


import java.io.*;

class Calisan {

	public void calis(int deger) throws IOException {
		System.out.println("Calisan calisiyor "+ deger);
	} 
}     

public class Sekreter extends Calisan {

	public void calis(int deger) throws FileNotFoundException, EOFException {
		
		System.out.println("Calisan calisiyor "+ deger);
		if(deger == 0) {
			throw new  FileNotFoundException("Dosyayi bulamadim");
		} else if(deger == 1) {
			throw new  EOFException("Dosyanin sonuna geldim");
		}  
	}


	public static void basla(Calisan c, int deger)  {
		try {
			c.calis(deger);   
		} catch (IOException ex) {
			System.out.println("Istisna olustu: "+ ex);
		}		
	}

	public static void main(String args[]) {
		Sekreter s1 = new Sekreter();
		Sekreter s2 = new Sekreter();
		Sekreter s3 = new Sekreter();
		basla(s1,2); //  sorunsuz
		basla(s1,1); //  EOFException
		basla(s3,0); //  FileNotFoundException
	}  
}

Bu örneğimizde Sekreter sınıfı Calisan sınıfından türemiştir. Ayrıca Sekreter sınıfının calis() metodu, kendisinin ana sınıfı olan Calisan sınıfının calis() metodunu iptal etmiştir. Calisan sınıfına ait calis() metodunun fırlatacağı istisna objesinin daha kapsamlı olmasındaki sebep yukarı doğru çevrimlerde (upcasting) sorun yaşanmaması içindir. Şimdi basla() metoduna dikkat edelim. Bu metod Calisan tipinde parametre kabul etmektedir, yani main() metodunun içerisinde oluşturulan Sekreter objeleri basla() metoduna parametre olarak gönderilebilir çünkü arada kalıtım (inheritance) ilişkisi vardır. 

Şekil-6

Fakat bu gönderilme esnasında bir daralma söz konusudur, Sekreter objeleri heap alanın dururken onlara bağlı olan değişkenlerin tipi Calisan tipindedir. Burada bir ayrıntı saklıdır, bu ayrıntı şöyledir: c.calis() komutu çağrıldığı zaman Calisan sınıfının basla() metoduna ait  etiketin altında Sekreter sınıfında tanımlı olan basla() metodunun gövdesindeki  kodlar çalıştırılır. Bu uygulamamızda kullanılan etiket aşağıdadır.

gösterim-12

public void calis(int deger) throws IOException {  // etiket

Çalıştırılacak gövde aşağıdadır.

gösterim-13

System.out.println("Calisan calisiyor "+ deger);
if(deger == 0) {
	throw new  FileNotFoundException("Dosyayi bulamadim");
} else if(deger == 1) {
	throw new  EOFException("Dosyanin sonuna geldim");
}  

Bu yüzden dolayı iptal edilen metodun olabilecek en kapsamlı istisna objesi fırlatması gerekir -ki yukarı doğru çevirim işlemlerinde (upcasting)  iptal eden metodların gövdelerinden fırlatılabilecek olan istisnalara karşı aciz kalınmasın. Olabilecek en kapsamlı istisna tipi bu uygulama örneğimizde IOException istisna tipiydi çünkü bu istisna tipi hem FileNotFoundException istisna tipini hem de EOFException kapsamaktadır(bkz) . Uygulamamızın çıktısı aşağıdaki gibidir.

Calisan calisiyor 2
Calisan calisiyor 1
Istisna olustu: java.io.EOFException: Dosyanin sonuna geldim
Calisan calisiyor 0
Istisna olustu: java.io.FileNotFoundException: Dosyayi bulamadim

İstisnaların sıralanması

Bir istisna catch bloğunda veya catch bloklarında yakalanırken, istisnaların hiyarerşik yapılarına dikkat edilmelidir.

ör-IstisnaSiralamasi.java

class IstisnaBir extends Exception {
} 

class IstisnaIki extends IstisnaBir {
}


public class IstisnaSiralamasi {  

    public static void main(String args[]) {
	  try {
	     throw new  IstisnaIki(); // dikkat
	  } catch (IstisnaIki is2) {
	      System.out.println("istisna yakalandi IstisnaIki: " );
	  } catch (IstisnaBir is1) {
	      System.out.println("istisna yakalandi IstisnaBir: " );
	  } 
    }
}

Bu örneğimizde kendimize özgü iki adet istisna tipi vardır. IstisnaIki sınıfı, IstisnaBir sınıfından türetilmiştir. Bunun anlamı eğer IstisnaIki tipinde bir istisna fırlatılırsa bunun IstisnaBir tipiyle catch bloğunda yakalanabileceğidir. Yukarıdaki örneğimizde IsitsnaIki tipinde bir istisna fırlatılmaktadır, fırlatılan bu istisna objesi ilk catch bloğunda yakalanmaktadır. Bir istisna bir kere yakalandı mı artık diğer catch bloklarının bu istisnayı bir daha tekrardan yakalama şansları yoktur. Yani bir istisna objesi bir kerede ancak bir catch bloğu tarafından yakalanabilir. Uygulamamızın çıktısı aşağıdaki aşağıdaki gibidir.

istisna yakalandi IstisnaIki:

Az önce bahsettiğimiz gibi eğer IstisnaIki tipinde bir istisna fırlatılırsa bu IstisnaBir tipiyle catch bloğunda yakalanabilir.

ör-IstisnaSiralamasi2.java


public class IstisnaSiralamasi2 {  

    public static void main(String args[]) {
	  try {
	     throw new  IstisnaIki(); // dikkat
	  } catch (IstisnaBir is1) {
	      System.out.println("istisna yakalandi IstisnaBir: " );
	  } 
    }
}

Yukarıdaki örneğimiz doğrudur. Uygulamamızın çıktısı aşağıdaki aşağıdaki gibidir.

istisna yakalandi IstisnaBir:

Eğer IstisnaIki tipinde bir istisna fırlatılırsa ve bu ilk etapda IstisnaBir tipiyle catch bloğunda ve ikinci etapda ise IstinsaIki tipiyle catch bloğunda yakalanmaya çalışırsa ilginç bir olay meydana gelir.

ör-IstisnaSiralamasi3.java


public class IstisnaSiralamasi3 {  

    public static void main(String args[]) {
	  try {
	     throw new  IstisnaIki(); // dikkat
	  } catch (IstisnaBir is1) {
	      System.out.println("istisna yakalandi IstisnaBir: " );
	  } catch (IstisnaIki is2) {
	      System.out.println("istisna yakalandi IstisnaIki: " );	
	  }
    }
}

Yukarıdaki örneğimizi derlemeye (compile) çalıştığımız zaman aşağıdaki hata mesajını alırız.

IstisnaSiralamasi3.java:9: exception IstisnaIki has already been caught
          } catch (IstisnaIki is2) {
            ^
1 error

Bu hata mesajının anlamı, ikinci catch bloğunun boşu boşuna konulduğu yönündedir çünkü zaten ilk catch bloğu, bu istisna objesini yakalıyabilir bu yüzden ikinci catch bloğuna hiçbir zaman erişimez.

Bölüm sonu

//...

Sorular

  1.   (aklınıza gelen bu konular ile ilgili güzel sorular varsa lütfen bildiriniz)

Mail Grubu

Java Kitap Projesinin mail grubuna üye olmak için = java_kitap_projesi-subscribe@yahoogroups.com 

Üyelikten çıkmak için =  java_kitap_projesi-unsubscribe@yahoogroups.com

Sorularınız ve Yorumlarınız için : upux@yahoo.com

 

Bu dökümanın her hakkı saklıdır.

© Copyright  2002