BÖLÜM 8
|
|
Altuğ
B. Altıntaş
© 2004 |
“Diğerlerinin yazdığı programda hata olabilir ama benim yazdığım programda hata olmaz....“
- Anonim
Bu bölümde istisnalar üzerinde durulacaktır. İstisna deyince aklınıza ne geliyor? Yanlış yazılmış uygulama mı? Beklenmeyen durum mu? Yoksa her ikisi de mi? İstisna demek işlerin sizin kontrolünüzden çıkması anlamına gelir. Yani kaos ortamı, önceden kestirilemeyen... Birşeylerin ters gitmesi sonucu uygulamanın normal akışına devam edememesi demektir. Bu ters giden bir şeyler ne olabilir? Örneğin kullanıcının uygulamanıza istemeyen veri girmesi olabilir veya açmak istediğiniz dosyanın yerinde olmaması olabilir, örnekleri çoğaltmak mümkündür. (yorum ekle)
Gerçekten tam bir uygulama yazmak ne demektir? Uygulamadan
beklenen görevleri yerine getirmesi onu tam bir uygulama yapar mı? Tabii ki
yapmaz. Uygulama zaten kendisinden beklenen işi yapmalı, aksi takdirde zaten
uygulama olmaz. Bir uygulamanın tam olmasının iki şartı vardır;
Birincisi uygulamanın kendisinden beklenen görevleri doğru bir şekilde
yerine getirmesidir yani doğruluk, ikincisi ise hatalı davranışlara
karşı dayanıklı olmasıdır, sağlamlık. Örneğin bizden iki sayıyı bölmek
için bir uygulama istense ne yapılmalıdır, A/ B - A bölüm B çok basit değil
mi?. İlk etapta karşı tarafın bizden istediği şey, girilen iki sayının doğru
şekilde bölünmesidir - doğruluk, bu öncelikli şarttır, bunda herkes hemfikir.
Peki ikinci şart nedir? İkinci şart ise sağlamlıktır, ikinci şart olan
sağlamlık genellikle önemsenmez. Bu örneğimizde karşı tarafın bizden istediği
olay, iki sayının bölünmesidir ama dikkat edin sayı dedim, kullanıcı int, double veya short ilkel tiplerinde sayı girilebilir. Peki ya
kullanıcı String bir
ifadeyi uygulamanıza yollarsa ne olur? veya A=5, B=0 girince uygulamanız buna
nasıl bir tepki verir? (Not :5/0=sonsuz) Uygulamanız direk olarak kapanır mı? Veya uygulamanız bu anlamsız ifadeleri
bölmeye mi çalışır? Eğer siz uygulamayı tasarlayan kişi olarak, bu hataları
önceden tahmin etmiş ve önlemleri almışsanız sorun ortaya çıksa bile, uygulama
için sorun olmaz ama gerçek dünyada her şeyi öngörebilmek imkansızdır. (yorum ekle)
Java programlama dili, oluşabilecek hatalara karşı sert bir
yaptırım uygular. Dikkat edin, oluşabilecek diyorum. Java programlama dili,
ortada hata oluşmasına sebebiyet verebilecek bir durum var ise yazılan Java
dosyasını derlemeyerek (compile) kodu yazan kişiye gerekli sert tavrı gösterir. Java programlama
dilinin bu tavrı doğru mudur? Kimileriniz diyebilir ki, "Java sadece
üstüne düşen görevi yapsın, oluşabilecek hataları bana söyleyerek canımı
sıkmasın". Bu yaklaşım yanlıştır, Java programlama dilinin amacı kodu yazan
kişiye maksimum şekilde yardımcı olmaktır, daha doğrusu insana dayalı
oluşabilecek hataları kendi üstüne alıp, hatalı uygulama üretimini minimuma
indirgemeyi amaçlayarak tasarlanmıştır. Bunun ilk örneğini çöp toplama (garbage collector) mekanizmasında görmüştük.
Diğer dillerde oluşturulan nesnelerin, daha sonradan işleri bitince bellekten silinmemelerinden
dolayı bellek yetmezlikleri oluşmaktadır. " Kodu yazan insan, oluşturduğu
nesneyi bellekten temizlemez mi? Ben bunu şahsen hiç yapmam. O zaman dalgın
insanlar kod yazmasın aaa! " diye bir söz sakın demeyin, çünkü
insanoğlu yeri geldiğinde çok dalgın olabilir ve bu dalgınlık uygulamayı bir
bellek canavarına dönüştürebilir ayrıca bu tür hataları, uygulamanın
içerisinden ayıklamak cidden çok zor bir iştir. Bu yüzden Java programlama
dilinde, bir nesnenin bellekten silinmesi kodu yazan kişiye göre değil, çöp
toplama algoritmalarına göre yapılır (bkz:Bölüm3). Java’nın oluşabilecek olan hatalara karşı bu
sert tutumu da gayet mantıklıdır. Bu sert tutum sayesinde ileride oluşabilecek
ve bulunması çok güç olan hataların erkenden engellenmesini sağlar. (yorum ekle)
İstisna oluşumuna en basit örnek olarak, yanlış kullanılmış dizi
uygulamasını verebiliriz. Java programlama dilinde dizilere erişim her zaman
kontrollüdür. Bunun anlamı, Java programlama dilinde dizilerin içerisine bir
eleman atmak istiyorsak veya var olan bir elemana ulaşmak istiyorsak, bu
işlemlerin hepsi Java tarafından önce bir kontrolden geçirilir. Bunun bir
avantajı, bir de dezavantajı vardır. Avantaj olarak güvenli bir dizi erişim
mekanizmasına sahip oluruz, dezavantaj olarak ufakta olsa hız kaybı meydana
gelir. Fakat böyle bir durumda hız mı daha önemlidir yoksa güvenlik mi? Bu
sorunun cevabı Java programlama dili için güvenliktir. Aşağıdaki örneğe dikkat
edelim; (yorum ekle)
Örnek: DiziErisim.java (yorum ekle)
public class DiziErisim {
public static void main(String args[]) {
int sayilar[] = {1, 2, 3, 4}; System.out.println("Basla"); for (int i=0 ; i < 5 ; i++) { System.out.println("--> " + sayilar[i]); } System.out.println("Bitti"); }} |
sayilar[], ilkel
(primitive) int tipinde dizi değişkenidir ve bağlı bulunduğu dizi nesnesinin
içerisinde 4 adet int tipinde eleman vardır. for
döngüsü sayesinde dizi içerisindeki elemanları ekrana bastırmaktayız. Bu
örneğimizdeki hata, for döngüsünün fazla dönmesiyle
dizinin olmayan elemanına ulaşmak istememizden kaynaklanmaktadır. Böyle bir hareket,
çalışma-anında (run-time) hata oluşmasına sebebiyet verip uygulamamızın aniden
sonlanmasına sebebiyet verecektir. Uygulamayı çalıştırıp, sonuçları hep beraber
görelim. (yorum ekle)
Basla--> 1--> 2--> 3--> 4Exception in thread "main"java.lang.ArrayIndexOutOfBoundsException at DiziErisim.main(DiziErisim.java:10)
Bu örneğimizdeki istisna, ArrayIndexOutOfBoundsException
istisnasıdır. Bu istisnanın sebebi, bir dizinin olmayan elemanına erişmeye
çalıştığımızı ifade eder. Fark edildiği üzere Java programlama dilinde, oluşan
istisnaları anlamak ve yerlerini belirlemek çok zor değildir. Örneğin bu
uygulamada istisnanın 10. satırda ortaya çıktığı anlaşılabilmektedir. (yorum ekle)
Bir uygulama içerisinde, başka ne tür istisnalar
oluşabilir ? Bir kaç örnek verirsek;
· Açmak istediğiniz fiziksel dosya
yerinde olmayabilir. (yorum ekle)
· Uygulamanıza kullanıcılar
tarafında, beklenmedik bir girdi kümesi gelebilir. (yorum ekle)
· Ağ bağlantısı kopmuş olabilir. (yorum ekle)
·
Yazmak istediğiniz dosya, başkası tarafından açılmış olduğundan yazma
hakkınız olmayabilir. (yorum ekle)
Olabilir, olmayabilir, belki... Yukarıdaki
istisnaların, bir uygulamanın başına gelmeyeceğini kim garanti edebilir?
Kimse, peki Java program içerisinde tam bir uygulama nasıl yazılır.
Başlayalım... (yorum ekle)
Bir istisna oluştuğu zaman uygulamamız aniden
kapanmak zorunda mı? Oluşan bu istisnayı daha şık bir şekilde yakalayıp uygulamanın
devam etmesini sağlamak mümkün mü? Cevap olarak evet; (yorum ekle)
Gösterim-8.1:
try {
// Istisnaya sebebiyet verebilecek olan kod } catch(Exception1 e1) {//Eger Exception1 tipinde istisna firlatilirsa buraya } catch(Exception2 e2) {//Eger Exception2 tipinde istisna firlatilirsa buraya } |
İstisnaya sebebiyet verebilecek olan kod, try bloğunun içerisinde tutularak güvenlik altına alınmış olur. Eğer
istisna oluşursa, istisna yakalama mekanizması devreye girer ve oluşan bu
istinanın tipine göre, uygulamanın akışı catch bloklarından birinin
içerisine yönlenerek devam eder. (yorum ekle)
İstisnalar nesnedir. Bir istisna oluştuğu
zaman bir çok olay gerçekleşir. İlk önce yeni bir istisna nesnesi belleğin heap
alında new() anahtar kelimesi ile
oluşturulur. Oluşan bu istisna nesnesinin içerisine hatanın oluştuğu satır
yerleştirilir. Uygulamanın normal seyri durur ve oluşan bu istisnanın
yakalanması için catch bloğunun olup olmadığına
bakılır. Eğer catch bloğu varsa uygulamanın akışı uygun
catch bloğunun içerisinden devam eder. Eğer catch bloğu tanımlanmamış ise hatanın oluştuğu yordamı (method)cağıran
yordama istisna nesnesi paslanır, eğer bu yordam içerisinde de istisnayı
yakalamak için catch bloğu tanımlanmamış ise
istina nesnesi bir üst yordama paslanır, bu olay böyle devam eder ve en sonunda
main() yordamına ulaşan istisna
nesnesi için bir catch bloğu aranır eğer bu
yordamın içerisinde de catch bloğu tanımlanmamış ise,
uygulananın akışı sonlanır. Bu olayları detaylı incelemeden evvel temel bir
giriş yapalım; (yorum ekle)
Örnek: DiziErisim2.java (yorum ekle)
public class DiziErisim2 {
public void calis() { int sayilar[] = {1,2,3,4}; for (int i=0 ; i < 5 ; i++) { try { System.out.println("--> " + sayilar[i]); } catch (ArrayIndexOutOfBoundsException ex) { System.out.println("Hata Olustu " + ex); } } // for } public static void main(String args[]) { System.out.println("Basla"); DiziErisim2 de2 = new DiziErisim2(); de2.calis(); System.out.println("Bitti"); }} |
Yukarıdaki uygulamamızda, dizi elemanlarına erişen
kodu try bloğu içerisine alarak,
oluşabilecek olan istinaları yakalama şansına sahip olduk. Sahip olduk da ne
oldu diyenler için gereken açıklamayı hemen yapalım. try-catch istisna yakalama mekanizması sayesinde istisna
oluşsa bile uygulamanın akışı aniden sonlanmayacaktır. DiziErisim.java
ile DiziErisim2.java uygulamalarının çıktısına bakılırsa aradaki
kontrolü hemen fark edilecektir. DiziErisim2.java uygulama örneğimizin
çıktısı aşağıdaki gibidir. (yorum ekle)
Basla--> 1--> 2--> 3--> 4Hata Olustu java.lang.ArrayIndexOutOfBoundsExceptionBitti
Kontrol nerede? Yukarıdaki DiziErisim2.java
uygulamasının çıktısının son satırına dikkat ederseniz, "Bitti" yazısının
ekrana yazıldığını görürsünüz oysaki bu ifade DiziErisim.java
uygulamasının çıktısında görememiştik. İşte kontrol buradadır. Birinci kuralı
daha net bir şekilde ifade edersek; try-catch istisna yakalama
mekanizması sayesinde, istisna oluşsa bile uygulamanın akışı aniden sonlanmaz.
(yorum ekle)
Yukarıdaki örneğimizde, try-catch mekanizmasını for döngüsünün içerisine
koyulabileceği gibi, for döngüsünü kapsayacak şekilde
de tasarlanıp yerleştirilebilir. (yorum ekle)
Örnek: DiziErisim3.java (yorum ekle)
public class DiziErisim3 {
public void calis() { try { int sayilar[] = {1,2,3,4}; for (int i=0 ; i < 5 ; i++) { System.out.println("--> " + sayilar[i]); } } catch (ArrayIndexOutOfBoundsException ex) { System.out.println("Hata Yakalandi"); } } public static void main(String args[]) { System.out.println("Basla"); DiziErisim3 de3 = new DiziErisim3(); de3.calis(); System.out.println("Bitti"); }} |
Bu uygulama örneği ile DiziErisim2.java
örneğimiz arasında sonuç bakımından bir fark yoktur. Değişen sadece tasarımdır,
try-catch bloğunun daha fazla kodu kapsamasıdır. (yorum ekle)
Bir yordam hangi tür istisna fırlatabileceğini
önceden belirtebilir veya belirtmek zorunda kalabilir. Bu yordamı (method)
çağıran diğer yordamlar da, fırlatılabilecek olan bu istisnayı, ya yakalarlar ya
da bir üst bölüme iletirler. Bir üst bölümden kasıt edilen, bir yordamı çağıran
diğer bir yordamdur. Şimdi bir yordamın önceden hangi tür istisna fırlatacağını
nasıl belirtmek zorunda kaldığını inceleyelim. (yorum ekle)
Örnek: IstisnaOrnek1.java (yorum ekle)
import java.io.*;
public class IstisnaOrnek1 { public void cokCalis() { File f = new File("ornek.txt"); BufferedReader bf = new BufferedReader( new FileReader( f ) ); System.out.println(bf.readLine()); } public void calis() { cokCalis(); } public static void main(String args[]) { IstisnaOrnek1 io1 = new IstisnaOrnek1(); io1.calis(); }} |
java.io paketinin içerisindeki sınıfları henüz incelemedik
ama bu örneğimizde kullanılan sınıfların ne iş yaptıklarını anlamak çok zor
değil. Burada yapılan iş, aynı dizinde bulunduğu farz edilen ornek.txt
dosyasının ilk satırını okumaya çalışmaktır. Yukarıdaki uygulamamızı derlemeye
çalışırsak, derleyicinin bize vereceği mesaj aşağıdaki gibi olur.
(yorum ekle)
IstisnaOrnek1.java:9: unreported exceptionjava.io.FileNotFoundException; must be caught or declared to be thrown new FileReader(f)); ^IstisnaOrnek1.java:10: unreported exception java.io.IOException; must be caught or declared to be thrown System.out.println(bf.readLine()); ^2 errors
Biz diskimizde bulunduğu varsayılan bir dosyaya
erişip onun ilk satırını okumaya çalışmaktayız. Çok masum gibi gözüken ama tehlikeli
istekler. Peki daha detaylı düşünelim ve oluşabilecek olan istisnaları tahmin
etmeye çalışalım. (yorum ekle)
İlk oluşabilecek olan istisna, o dosyanın yerinde olmayabileceğidir.
Bu beklenmeyen bir durum oluşturabilir, başka neler olabilir? Bundan ayrı
olarak biz sanki o dosyanın orada olduğundan eminmişiz gibi birde onun ilk
satırını okumaya çalışıyoruz, bu isteğimizde istisnaya sebebiyet verebilir
çünkü dosya yerinde olsa bile dosyanın ilk satırı olmayabilir. Dikkat ederseniz
hep olasılıklar üzerinde durmaktayım ama güçlü olasılıklar. Peki bu uygulamayı
derlemenin bir yolu yok mu? (yorum ekle)
Az önce bahsedildiği gibi bir yordam içerisinde
oluşmuş olan istisnayı bir üst bölüme yani o yordamı çağıran yordama
fırlatabilir. Eğer bir istisna oluşursa bu anlattıklarımıza göre bir
yordamın iki şansı vardır diyebiliriz. Birincisi oluşan bu istisnayı ya
yakalayıp gereken işlemleri kendi içerisinde sessizce gerçekleştirebilir veya
bu istisna ile ben ne yapacağımı bilmiyorum beni çağıran yordam düşünsün diyip,
istisna nesnesini bir üst bölüme fırlatabilir. (yorum ekle)
Aşağıdaki örnekte, oluşan istisnayı aynı yordamın
içerisinde yakalanmaktadır; bu yüzden yordamın hangi istisnayı
fırlatabileceğini açıklamasına gerek yoktur. Bir yordamın hangi tür istisnayı
nasıl fırlatabileceğini açıklama olayını az sonra göreceğiz ama önce aşağıdaki
örneğimizi inceleyelim. (yorum ekle)
Örnek: IstisnaOrnek2.java (yorum ekle)
import java.io.*;
public class IstisnaOrnek2 { public void cokCalis() { try { File f = new File("ornek.txt"); BufferedReader bf=new BufferedReader(new FileReader(f) ); System.out.println(bf.readLine()); } catch (IOException ex) { System.out.println("Hata Yakalandi =" + ex); } } public void calis() { cokCalis(); System.out.println("calis() yordamı"); } public static void main(String args[]) { IstisnaOrnek2 io2 = new IstisnaOrnek2(); io2.calis(); System.out.println("main() yordamı"); }} |
Verilen örnekte, dosyaya erişirken veya ondan
birşeyler okumak isterken oluşabilecek olan istisnalar; java.io.IOException
istisna tipini kullanarak yakalanabilir. Zaten IstisnaOrnek1.java
uygulamasının derlemeye çalışırken alınan hatadan hangi tür istisna tipinin kullanılması
gerektiğini de çıkartabiliriz. java.io.FileNotFound Exception istina
tipini, java.io.IOException tipi kullanılarak yakalanabilir bunun nasıl
olduğunu biraz sonra göreceğiz. (yorum ekle)
Yukarıdaki uygulama güzel bir şekilde derlenir çünkü oluşabilecek olan tüm istisnalar için tedbir alınmıştır. Olayların akışını inceliyelim, bu uygulamayı çalıştırdığımız zaman (java IstisnaOrnek2) ilk olarak main() yordamından akışa başlanır. Daha sonra calis() yordamının ve cokCalis()