EXCEPTION HANDLING IN JAVA

Emre Dinç
10 min readOct 29, 2022

--

Selamlar, ilk medium yazımda sizlere Java’da Exception Handling konusunu tüm detayları ve örnekleriyle beraber anlatmaya çalışacağım. Her türlü yapıcı eleştiriye açığım. Bu dokümanı hazırlamak benim için çok faydalı oldu. Umarım sizler için de faydalı olur. Yavaş yavaş konumuza geçelim…

Exception nedir ?

Bir programın yürütülmesi sırasında, yani run-time’da meydana gelen ve programın normal akışını bozan, istenmeyen veya beklenmeyen bir event olarak tanımlayabiliriz.

Exception bir method içerisinde oluşursa, bir nesne (object) oluşturur. Bu nesneye exception nesnesi denir. Bu nesne oluşan exception’a ait bilgiler içerir. Örneğin; exception’ın adı, exception’ın açıklaması ve exception oluştuğunda programın durumu.

Exception Handling nedir ?

Exception’lar program tarafından yakalanabilir ve handle edilebilir. Buna Exception Handling deriz. Java’da Exception Handling, uygulamanın düzenli akışının korunabilmesi için run-time error’ları handle etmenin etkili yollarından biridir. Java Exception Handling, ClassNotFoundException, IOException, SQLException, RemoteException vb. gibi run-time errorları handle etmek için bir mekanizmadır.

Neden ihtiyaç duyarız ?

Bir senaryo düşünelim; aşağıdaki görseldeki gibi bir programımız var. Java satır satır işleyen bir programlama dili olduğu için kodu ilk satırdan yürütmeye başlayacak.

Burada farazi olarak bir program akışı belirttik.

Üçüncü satıra geldiğimizde exception oluştuğunu farz edelim. Bu durumda program anormal şekilde sonlanacak ve geri kalan kod satırları yürütülmeyecek. Bu senaryoyu engelleyebilmek için Exception Handling’e ihtiyaç duyarız.

Başlıca Exception oluşma nedenleri:

Hatalar (Errors):

Error’lar, Java sanal makinesinin (JVM, Java Virtual Machine) belleğinin tükenmesi (Running out of memory), bellek sızıntıları (Memory leak), yığın taşması hataları (Stack over flow errors), kitaplık uyumsuzluğu (Library incompatibility), sonsuz özyineleme (Infinite recursion) gibi düzeltilemez durumları temsil eder. Error’lar genellikle programcının kontrolü dışındadır ve hataları handle etmeye çalışmamalıyız.

Exception Hiyerarşisi

Tüm exception ve error türleri, hiyerarşinin temel sınıfı olan Throwable sınıfının alt sınıflarıdır. Throwable sınıfı, Exception ve Error olmak üzere iki ana kola ayrılır.

Exception, kullanıcı programlarının yakalaması gereken istisnai koşullar için kullanılır. Örneğin, NullPointerException.

Error, Java Run-time sistemi (JVM) tarafından, run-time ortamının kendisiyle (JRE) ilgili error’ları belirtmek için kullanılır. Örneğin, StackOverFlowError.

Exception Türleri

Java’da hali hazırda bulunan exception türleri vardır. Ayrıca, kullanıcıların kendi exception’larını tanımlamalarına da izin verir.

Built-in Exceptions:

Hali hazırda Java kütüphanelerinde bulunan exception türlerine denir. Bu exception’lar belirli error durumlarını açıklamak için kullanılır.

Checked Exceptions:

Compile-time Exceptions olarak da adlandırılır. Çünkü bu excepiton türü derleme zamanında (compile-time) derleyici (compiler) tarafından kontrol edilir.

Unchecked Exceptions:

Run-time Exceptions olarak da adlandırılır. Çünkü compiler, compile-time’da bu exceptionları kontrol etmeyecektir. Basitçe açıklayacak olursak, bir program bir unchecked exception fırlatırsa ve biz onu handle veya declare etmemiş olsak bile, program bir derleme hatası vermez. Java’da Error ve Runtime Exception sınıfları altındaki exception’lar denetlenmeyen (unchecked) exception’lardır.

Not: Hiyerarşiye göre Unchecked Exception’lar hariç Throwable sınıfı altındaki tüm sınıflar denetlenir.

User Defined Exceptions:

Bazen hali hazırda Java’da kütüphanelerinde bulunan exception türleri spesifik bir istisna durumunu tanımlayamaz. Bu gibi durumlarda, kullanıcılar kendi Exception sınıflarını oluşturabilir. Buna User-Defined Exceptions denir.

Kendi exception sınıfımızı yazarken nelere dikkat etmeliyiz ?

  • Tüm exception’lar Throwable’ın child class’ı olmalı.
  • Handle veya Declare Rule tarafından otomatik olarak uygulanan bir checked exception yazmak istiyorsanız, Exception sınıfını extend etmelisiniz.
  • Ayrıca user defined exception’lar, Exception sınıfından extend ettiği için Checked Exception sayılırlar.
  • Bir run-time exception yazmak için, RuntimeException sınıfını extend etmemiz gerekir.

Java’da exception’ları ve error’ları iki kategoride tanımlamak mümkün:

  1. JVM Exceptions: JVM tarafından özel veya mantıksal olarak oluşturulan exception’lar/error’lardır. Örneğin; NullPointerException, ArrayIndexOutOfBoundsException ve ClassCastException.
  2. Programmatic Exceptions: Application veya API programcıları tarafından açıkça throw edilir. Örneğin; IllegalArgumentException, IllegalStateException.

Exception Handle Avantajları:

  • Programın yürütülmesinin tamamlanmasını sağlar.
  • Anlamlı hata yorumlamamızı sağlar.
  • Hata türlerini belirlemeyi sağlar.

Exception Methods

Throwable Class’ta bulunan kullanılabilir metodlardır.

public String getMessage()

  • Meydana gelen exception hakkında detaylı mesaj return eder. Bu mesaj Throwable Constructor içerisinde initialize edilir.

public Throwable getCause()

  • Throwable objesi tarafından temsil edilen exception’ın nedenini return eder.

public String toString()

  • getMessage() metodunun sonucuyla birleştirilmiş sınıfın adını döndürür.

public void printStackTrace()

  • toString() metodunun sonucunu yığın izlemeyle (stack trace) beraber System.err’ye, yani hata çıktı akışına (error output stream) yazdırır.

public StackTraceElement [] getStackTrace()

  • Yığın izlemedeki her öğeyi içeren bir dizi return eder. 0 dizinindeki öğe, çağrı yığınının üstünü temsil eder ve dizideki son öğe, çağrı yığınının altındaki metodu temsil eder.
  • printStackTrace() metodu ile oluşan hata satırlarını StackTraceElement tipindeki array objesine çevirir. Oluşan exception’a ait yol haritasını bir StackTraceElement dizisi şeklinde sunar.
  • printStackTrace() metodunun çıktısını hayal edelim. Buradaki ilk satır, bu dizinin ilk elemanına, son satır son elemanına denk gelir.

public Throwable fillInStackTrace()

  • Meydana gelen exception’ın yol haritasını Throwable nesnesi içerisinde elde etmemiz için bu metodu kullanmalıyız. Bu exception’ın tekrar yaşanması durumunda yararlı olur.

public String getLocalizedMessage()

  • getMessage() ile aynı sonucu döndürür. getMessage() metodundan farklı olarak , alt Exception sınıfları bu metodu override ederek hata mesajını lokalize edebilir.

JVM EXCEPTION’I NASIL HANDLE EDER ?

Default Exception Handling: Bir metodun içerisindeyken, bir exception meydana gelirse, metod, exception nesnesi olarak bilinen bir nesne oluşturur ve bunu run-time system’e (JVM) devreder. Bu exception nesnesi, exception’ın adını, açıklamasını ve exception’ın meydana geldiği programın mevcut durumunu içerir. Bir exception nesnesi oluşturma ve run-time sistemde handle etmeye Exception Fırlatma denir.

Bir exception’ın oluştuğu metoda ulaşmak için çağırılan metodların bir listesi olabilir. Bu listeye Call Stack denir. Bir exception oluştuğunda aşağıdaki prosedür gerçekleşir.

• Run-time system, meydana gelen exception’ı handle edebilecek kod bloğuna sahip metodu bulmak için Call Stack içerisinde araştırmaya başlar. Bu kod bloğuna Exception Handler denir.

• Run-time system, exception’ın oluştuğu metoddan başlayarak, metodların çağırıldığı sıranın tersi sırayla Call Stack boyunca ilerler.

• Uygun bir handler bulursa, oluşan exception’ı, tıpkı bir argümanın bir metod parametresine iletilmesi gibi, bu handler’ın catch bloğuna iletilir. Uygun handler’dan kastımız şudur; fırlatılan exception nesnesinin türünün, handle edebileceği exception’ın nesnesinin türüyle eşleştiği anlamına gelir.

• Run-time system, Call Stack’teki tüm metodları ararsa ve uygun bir handler bulamazsa, exception nesnesini default exception handler’a devreder. Bu handler, exception bilgilerini aşağıdaki şekilde yazdırır ve programı anormal şekilde sonlandırır.

“Exception in thread ‘xxx’ Name of Exception: ‘exception açıklaması’
... ...... .. // Call Stack

PROGRAMCI EXCEPTION’I NASIL HANDLE EDER ?

Sistem tarafından oluşturulan exceptionlar, Java Run-time System tarafından otomatik fırlatılır. Bir exception’ı manuel olarak fırlatmak için, customized exception handling için önemli 5 anahtar kelimeden biri olan ‘throw’ kullanılmalıdır. Kalan 4 anahtar kelimemiz ise şunlardır; try, catch, throws ve finally. Exception oluşturabileceğini düşündüğümüz program ifadeleri try bloğu içerisinde yazılır. Try bloğu içerisinde bir exception olursa fırlatılır. Kodumuz catch bloğunu kullanarak bu exception’ı yakalayıp handle edebilir.

Bir try bloğu tamamlandıktan sonra kesinlikle yürütülmesi gereken herhangi bir kod varsa finally bloğu içerisinde yazılmalıdır.

Try-catch bloğunun anlaşılması için bir örnek üzerinden gidelim.

Yukarıdaki örnekte bir array tanımlanmış ve boyutu (size) 4 olarak belirlenmiş. Bu demek oluyor ki array 0 ile 3 arasında indexlere sahip. Fakat 4. indexte bir elemana erişmeye çalışıyoruz. Bu satırda exception fırlatılıyor. Bu durumda JVM programı anormal şekilde sonlandırır ve System.out.println asla yürütülmez. Try-catch kullanarak exception handle edilmeli.

Peki try-catch nasıl kullanılır ?

Try bloğundaki kod parçacığında bir exception oluşursa, exception listedeki ilk catch bloğuna atılır.

  • Atılan exception’ın veri türü ExceptionType1 ile eşleşirse, orada yakalanır.
  • Eşleşmezse, exception ikinci catch bloğuna geçer.

Bu exception, yakalanana kadar veya tüm catch bloklarından geçene kadar devam eder. Hiçbir catch bloğunda yakalanmazsa, mevcut metod yürütmeyi durdurur ve exception, Call Stack’te bir önceki metoda atılır.

✎ Not: Java 7'den bu yana tek bir catch blok kullanarak birden fazla exception handle edebiliyoruz. Bu özellik (feature) kodu daha sade bir hale getirmemizi sağlıyor.

Catch bloğuna birden fazla exception yazmanın örneği

✎ Hatırlanması gereken önemli noktalar:

  • Bir method’da exception oluşturabilecek birden fazla ifade olabilir. Bu nedenle tüm ifadeleri kendi try bloklarına koymalı ve her biri için kendi catch bloklarında ayrı bir exception handler sağlamalıyız.
  • Catch bloğunda kullanacağımız Exception sınıfı Throwable sınıfından miras (inheritance) almalıdır.
  • Try ifadesi olmadan catch bloğu tek başına var olamaz.
  • Try-catch-finally blokları arasında herhangi bir kod bulunamaz.
  • Her try bloğu için sıfır veya daha fazla catch bloğu olabilir ancak yalnızca bir tane finally blok olabilir.
  • Her try bloğunu bir catch veya bir finally bloğu takip etmelidir. Yani try bloğu catch veya finally olmadan tek başına var olamaz.
  • Finally blok kullanımı isteğe bağlıdır. Exception oluşursa; try ve catch bloklarından sonra, oluşmazsa; try bloğundan sonra yürütülür.

THROW VE THROWS FARKLARI NELERDİR ?

Java’daki throw anahtar kelimesi bir metoddan veya kod bloğundan açıkça bir exception fırlatmak için kullanılır. Checked veya Unchecked exception fırlatabiliriz.

Throw anahtar kelimesinin ana kullanım amacı özel (custom) exceptionlar fırlatmaktır. Ancak bu exception, Throwable türünde veya Throwable’ın bir alt sınıfı olmalıdır. Örneğin Exception, Throwable’ın alt sınıfıdır ve kullanıcı tanımlı (user defined) exception’lar Exception sınıfını extend eder.

  • C++’tan farklı olarak int, char, floats veya non-throwable sınıfları gibi veri türleri exception olarak kullanılamaz.
  • Throw ifadesi execute edildiği anda programın akışı durur ve Exception türüyle eşleşen catch bloğu bulmak için en yakınındaki catch bloğu kontrol edilir. Eğer varsa exception catch’e aktarılır. Aksi takdirde bir sonraki catch blok kontrol edilir ve böyle devam eder. Hiç eşleşme olmazsa Default Exception Handler programı durdurur.

Throws keyword metodun imzasında (signature) kullanılan ve bu metodun listelenen exception’lardan birini fırlatabileceğini belirtmek için kullanılır. Böyle bir metod çağırmamız gerekiyorsa try-catch bloğu kullanarak handle etmeliyiz.

tür metod_ismi (parametreler) throws exception_listesinot: exception_listesi dememizin sebebi, virgül ile
ayırarak birden fazla exception yazabiliriz.

Bir programda exception oluşma ihtimali varsa , derleyici (compiler) compile-time error verir. Zorunlu olarak bu checked exception’ı handle etmeliyiz. Aksi takdirde declare edilmemiş exception’ın yakalanması veya fırlatılması için declare edilmesi gerektiğini söyleyen compile-time error alırız. Bu compile-time error’u önlemek için exception’ı iki şekilde handle edebiliriz.

  1. try-catch kullanmak
  2. throws keyword kullanmak

Yukarıdaki görselde exception durumu declare edilmediği için compile-time error alırız. Çünkü main thread uyursa, diğer thread’lerin main() metodu execute etme ihtimali vardır. Bu da InterruptedException’a neden olur.

Yukarıdaki görselde exception ihtimalinin declare edildiğini görüyoruz. Exception oluşma ihtimali handle edildiği için program durmaz ve System.out.println() çıktısını konsolda görürüz.

Hatırlanması gerekenler:

  • Throws keyword sadece checked exceptionlar için gereklidir. Unchecked’ler için kullanılması anlamsızdır.
  • Yalnızca compiler’ı ikna etmek için gereklidir. Programın anormal şekilde sonlandırılmasını engellemez. Yani run-time’da herhangi bir sebeple exception yaşanırsa programın sonlanmasını engellemez.
  • Throws keyword sayesinde, metodu kullanmak isteyecek bir başka kişiye exception ihtimali hakkında bilgi vermiş oluruz.
  • Throws keyword’ü ile virgülle ayırarak birden fazla exception’da bildirebiliriz. Örneğin:
throws RemoteException, InsufficientFundsException {// herhangi bir kod bloğu}

TRY-WITH-RESOURCES

Exception’lardan bahsederken değinmemiz gereken bir diğer konu da kaynakların kullanımıdır. Genellikle Stream’ler veya Connection’lar gibi kaynaklar kullandığımız zaman finally blok kullanarak bu kaynakları kapatmamız gerekir.

Aşağıdaki örnek kodda FileReader kullanarak data okuyoruz ve finally blok kullanarak kapatıyoruz.

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {

public static void main(String args[]) {
FileReader fr = null;
try {
File file = new File("file.txt");
fr = new FileReader(file); char [] a = new char[50];
fr.read(a); // Array'in içeriğini okur
for(char c : a)
System.out.print(c); // Karakterleri birer birer yazdırır
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fr.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}

Automatic Resource Management yani otomatik kaynak yönetimi olarak da adlandırılan try-with-resources, Java 7'de tanıtılan ve try/catch bloğu içinde kullanılan kaynakları otomatik olarak kapatan yeni bir exception handling mekanizmasıdır. Bu ifadeyi kullanmak için parantez içinde gerekli kaynakları bildirmeniz yeterlidir. Böylece oluşturulan resource, bloğun sonunda otomatik olarak kapatılacaktır.

Syntax aşağıdaki gibidir:

try(FileReader fr = new FileReader("file path")) {
// Kaynağın kullanıldığı yer
} catch () {
// Catch blok
}
}

Gördüğünüz üzere kaynağı kapatmak için ‘close()’ metodu kullanmak zorunda kalmadık. Parantez içinde kaynağın yolunu (path) belirttiğimiz zaman bloğun sonunda otomatik olarak kapanır.

✎ Hatırlanması gerekenler:

  • Try-with-resources ile bir class kullanmak için, AutoCloseable Interface’i implemente etmesi gerekir. Bu interface’e ait close() metodu çalışma zamanında (runtime) otomatik olarak çağırılır.
  • Try-with-resources ifadesinde birden fazla class bildirebiliriz. Bu sınıflar kapatılırken ters sırada kapatılır. (son bildirilen ilk kapatılır)
  • Parantez içinde yapılan Kaynak Bildirimi (Resource Declaration) hariç her şey, normal bir try/catch bloğu ile aynıdır.
  • Try içinde declare edilen kaynak (resource), try bloğunun başlangıcından hemen önce nesnesi üretilir (instantiated).
  • Try bloğunda declare edilen kaynak, Java tarafından arka planda ‘final’ olarak declare edilir. Yani gözümüzle orada final keyword görmeyiz ama Java declare edilen bu kaynağı final olarak ele alır.

— — — — — EXCEPTION KOD ÖRNEKLERİ — — — — —

1) Checked Exception Örnekleri:

Örnek 1:

  • Bu kod compile olmazdı çünkü main metod, FileReader() constructor metodunu kullanıyor ve bu metod Checked Exception türlerinden FileNotFoundException fırlatır. Ayrıca readLine() ve close() metodları da checked exception türü olan IOException fırlatır.
  • Göründüğü üzere ‘throws IOException’ yazılırsa bu kod compile olur. ✎Not: FileNotFoundException, IOException’ın alt sınıfı olduğu için ayrıca belirtmeye gerek yok.

2) Unchecked Exception Örnekleri:

Örnek 1:

  • Bu kodun derlenmesinde bir sorun olmaz. Fakat çalıştırıldığında ArithmeticException fırlatır. Çünkü unchecked bir exception olduğu için derleyici (compiler) kodu sorunsuz compile eder.

Örnek 2:

— — — — — KAYNAKÇA — — — — —

  1. https://www.javatpoint.com/exception-handling-in-java
  2. https://www.geeksforgeeks.org/exceptions-in-java/
  3. https://www.tutorialspoint.com/java/java_exceptions.htm
  4. https://docs.oracle.com/en/java/
  5. Kitap: JAVA Programlama Dili ve Yazılım Tasarımı Yazar: Altuğ B. ALTINTAŞ Editör: Rifat ÇÖLKESEN
  6. Kitap: KODLAB Java Yeni Başlayanlar İçin Yazar: Mehmet KİRAZLI — Sezer TANRIVERDİOĞLU

--

--

Emre Dinç
Emre Dinç

Written by Emre Dinç

Java Backend Developer | @DincDev on Twitter

Responses (2)