|
~BöLÜM-8~ |
|
Altuğ B. Altıntaş ©2002 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
İstisnalar (Exceptions)
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 ;
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.
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 ;
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
İ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
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.
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
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 İfadeleriBir 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.
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.
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
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;
Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.
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
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
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;
Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.
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
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;
Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.
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
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;
Uygulamamızın toplu olarak çıktısı aşağıdaki gibidir.
"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şisiNası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.
Tüm diğer Exception istisna tiplerini yakalamakBir uygulama içerisinde oluşabilecek olan tüm diğer Exception tiplerini yakalamak için aşağıdaki ifadeyi kullanabilirsiniz. gösterim-3
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 tipleriDiziErisim.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 ;
gösterim-4
gösterim-5
gösterim-6
gösterim-7
Ö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.
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.
Uygulamamızın çıktısı aşağıdaki gibi olur.
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
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.
gösterim-8
Uygulamamızın çıktısı aşağıdaki gibi olur.
Java 1.4 ile gelen bir başka yenilik ise initCause() metodudur. Bu metod iki istisna tipini birleştirmeye yarar. ör-IstisnaMetodlari3.java
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.
Uygulamamızın çıktısı aşağıdaki gibi olur.
Oluşan bir hatanın yol haritasını printStackTrace() metodu sayesinde görebilirsiniz. ör-IstisnaMetodlari4.java
Uygulamamızın çıktısı aşağıdaki gibi olur.
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
Uygulamamızın çıktısı aşağıdaki gibi olur.
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
Uygulamamızın çıktısı aşağıdaki gibi olur.
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
Uygulamamızın çıktısı aşağıdaki gibi olur.
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
İ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
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
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
Yukarıdaki örneğimizde üç adet harekete kızılmaktadır. Bunlar sırasıyla :
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.
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ğuBir 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
Ne olursa olsun calışmasını istediğiniz kodu finally bloğuna yazabilirsiniz. Bir uygulama üzerinde açıklamaya çalışırsak . ör-FinallyOrnek1.java
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.
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
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
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
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.
return ve finally bloğufinally 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
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.
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
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
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
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.
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
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.
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
Yukarıdaki örneğimizi derlemeye (compile) çalıştığımız zaman aşağıdaki hata mesajı ile karşılaşırız.
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
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.
Uygulamanın çıktısından da anlaşılağı üzere, tekrar eden kısmın kaç kere tekrar ettiği bilgisi de verilmektedir. Mesela :
Bu kısım 1 kere tekrar etmiştir aynı şekilde
Bu kısım ise 3 kere tekrar etmiştir. İlginç gelişmeOluşan bir istisna her zaman fırlatılamıyabilir. Aşağıdaki uygulamamızı inceliyelim ör-FirlatimOrnek1.java
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.
Ö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
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.
Oluşan istisna, basla() metodunda yakalanmıştır
ama daha sonra ortalardan kaybolmuştur ör-FirlatimOrnek3.java
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.
İ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
AB.java uygulamasını derlemeye (compile) çalıştığımız zaman aşağıdaki hata mesajını alırız.
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
İş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
İ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
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
Çalıştırılacak gövde aşağıdadır. gösterim-13
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.
İ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
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.
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
Yukarıdaki örneğimiz doğrudur. Uygulamamızın çıktısı aşağıdaki aşağıdaki gibidir.
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
Yukarıdaki örneğimizi derlemeye (compile) çalıştığımız zaman aşağıdaki hata mesajını alırız.
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
Bu dökümanın her hakkı saklıdır. © Copyright 2002 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||