Java'daki istisnalar, Bölüm 1: Özel durum işlemeyle ilgili temel bilgiler

Java istisnaları, program hatasını temsil etmek ve çözmek için kullanılan kitaplık türleri ve dil özellikleridir. Başarısızlığın kaynak kodda nasıl temsil edildiğini anlamak istiyorsanız, doğru yere geldiniz. Java istisnalarına genel bir bakışa ek olarak, Java'nın nesneleri fırlatmak, başarısız olabilecek kodu denemek, fırlatılan nesneleri yakalamak ve bir istisna atıldıktan sonra Java kodunu temizlemek için dil özelliklerini kullanmaya başlayacağım.

Bu öğreticinin ilk yarısında, Java 1.0'dan beri var olan temel dil özellikleri ve kitaplık türleri hakkında bilgi edineceksiniz. İkinci yarıda, daha yeni Java sürümlerinde sunulan gelişmiş yetenekleri keşfedeceksiniz.

Bu eğitimdeki kod örneklerinin JDK 12 ile uyumlu olduğunu unutmayın.

indir Kodu alın Bu eğitimdeki örnek uygulamalar için kaynak kodunu indirin. JavaWorld için Jeff Friesen tarafından düzenlendi.

Java istisnaları nelerdir?

Hata, bir Java programının normal davranışı beklenmeyen davranışla kesintiye uğradığında ortaya çıkar. Bu ayrışma bir istisna olarak bilinir . Örneğin, bir program içeriğini okumak için bir dosyayı açmaya çalışır, ancak dosya mevcut değildir. Java, istisnaları birkaç türe sınıflandırır, bu yüzden her birini ele alalım.

Kontrol edilen istisnalar

Java, harici faktörlerden (eksik dosya gibi) kaynaklanan istisnaları kontrol edilen istisnalar olarak sınıflandırır . Java derleyicisi, bu tür istisnaların meydana geldikleri yerde ele alındığını (düzeltildiğini) veya başka bir yerde işlenmek üzere belgelendiğini kontrol eder.

Özel durum işleyiciler

Bir istisna işleyicisi , bir özel durum işleyen kod dizisidir. Bağlamı sorgular - yani istisna oluştuğu sırada kapsamda olan değişkenlerden kaydedilen değerleri okur - daha sonra öğrendiklerini, Java programını normal davranış akışına geri yüklemek için kullanır. Örneğin, bir istisna işleyici kaydedilmiş bir dosya adını okuyabilir ve kullanıcıdan eksik dosyayı değiştirmesini isteyebilir.

Çalışma zamanı (kontrol edilmemiş) istisnaları

Bir programın bir tamsayıyı 0 tamsayısına bölmeye çalıştığını varsayalım. Bu imkansızlık, başka bir tür istisnayı, yani çalışma zamanı istisnasını gösterir . Kontrol edilen istisnaların aksine, çalışma zamanı istisnaları genellikle kötü yazılmış kaynak kodundan kaynaklanır ve bu nedenle programcı tarafından düzeltilmelidir. Derleyici, çalışma zamanı istisnalarının başka bir yerde işlendiğini veya işlenmek üzere belgelendiğini kontrol etmediğinden, bir çalışma zamanı istisnasını kontrol edilmeyen bir istisna olarak düşünebilirsiniz .

Çalışma zamanı istisnaları hakkında

Bir programı, bir çalışma zamanı istisnasını işleyecek şekilde değiştirebilirsiniz, ancak kaynak kodunu düzeltmek daha iyidir. Çalışma zamanı istisnaları genellikle geçersiz argümanların bir kitaplığın yöntemlerine iletilmesinden kaynaklanır; buggy arama kodu düzeltilmelidir.

Hatalar

Bazı istisnalar çok ciddidir çünkü bir programın çalışmaya devam etme yeteneğini tehlikeye atarlar. Örneğin, bir program JVM'den bellek ayırmaya çalışır, ancak isteği karşılamak için yeterli boş bellek yoktur. Başka bir ciddi durum, bir program bir Class.forName()yöntem çağrısı aracılığıyla bir sınıf dosyasını yüklemeye çalıştığında , ancak sınıf dosyası bozuk olduğunda ortaya çıkar. Bu tür bir istisna, hata olarak bilinir . Hataları asla kendi başınıza halletmeye çalışmamalısınız çünkü JVM ondan kurtaramayabilir.

Kaynak kodundaki istisnalar

Bir istisna, kaynak kodda bir hata kodu veya bir nesne olarak gösterilebilir . İkisini de tanıtacağım ve size nesnelerin neden üstün olduğunu göstereceğim.

Hata kodları nesnelere karşı

C gibi programlama dilleri , başarısızlığı ve başarısızlık nedenlerini, yani istisnaları temsil etmek için tamsayı tabanlı hata kodlarını kullanır. Burada bir çift örnek var:

if (chdir("C:\\temp")) printf("Unable to change to temp directory: %d\n", errno); FILE *fp = fopen("C:\\temp\\foo"); if (fp == NULL) printf("Unable to open foo: %d\n", errno);

C'nin chdir()(dizini değiştir) işlevi bir tamsayı döndürür: başarı durumunda 0 veya başarısızlık durumunda -1. Benzer şekilde, C'nin fopen()(dosya açma) işlevi başarı durumunda bir yapıya boş olmayan bir işaretçi (tamsayı adresi) FILEveya başarısızlık durumunda bir boş (0) gösterici (sabit ile temsil edilir) döndürür NULL. Her iki durumda da, başarısızlığa neden olan istisnayı belirlemek için, genel errnodeğişkenin tam sayıya dayalı hata kodunu okumanız gerekir .

Hata kodları bazı problemler ortaya çıkarır:

  • Tam sayılar anlamsızdır; temsil ettikleri istisnaları tanımlamazlar. Örneğin, 6 ne anlama geliyor?
  • Bağlamı bir hata koduyla ilişkilendirmek garip. Örneğin, açılamayan dosyanın adını yazdırmak isteyebilirsiniz, ancak dosyanın adını nerede saklayacaksınız?
  • Tam sayılar gelişigüzeldir ve kaynak kodunu okurken kafa karışıklığına neden olabilir. Örneğin, başarısızlığı test etmek yerine belirtmek if (!chdir("C:\\temp"))( !DEĞİL anlamına gelir) if (chdir("C:\\temp"))daha açıktır. Ancak, başarıyı göstermek için 0 seçilmiştir ve bu nedenle if (chdir("C:\\temp"))başarısızlığı test etmek için belirtilmelidir.
  • Hata kodlarının göz ardı edilmesi çok kolaydır, bu da hatalı kodlara yol açabilir. Örneğin, programcı kontrolü belirleyebilir chdir("C:\\temp");ve yok sayabilir if (fp == NULL). Dahası, programcının incelemesine gerek yoktur errno. Başarısızlığı test etmeyerek, her iki işlev de bir başarısızlık göstergesi döndürdüğünde program kararsız davranır.

Bu sorunları çözmek için Java, istisna işlemeye yönelik yeni bir yaklaşım benimsedi. Java'da, istisnaları tanımlayan nesneleri, bu nesneleri fırlatmaya ve yakalamaya dayalı bir mekanizma ile birleştiriyoruz. İstisnaları belirtmek için nesne yerine hata kodu kullanmanın bazı avantajları şunlardır:

  • Anlamlı bir ada sahip bir sınıftan bir nesne oluşturulabilir. Örneğin, FileNotFoundException( java.iopakette) 6'dan daha anlamlıdır.
  • Nesneler bağlamı çeşitli alanlarda depolayabilir. Örneğin, bir mesajı, açılamayan dosyanın adını, ayrıştırma işleminin başarısız olduğu en son konumu ve / veya bir nesnenin alanlarındaki diğer öğeleri depolayabilirsiniz.
  • ifBaşarısızlığı test etmek için ifadeler kullanmazsınız . Bunun yerine, istisna nesneleri program kodundan ayrı bir işleyiciye atılır. Sonuç olarak, kaynak kodun okunması daha kolaydır ve hatalı olma olasılığı daha düşüktür.

Atılabilir ve alt sınıfları

Java, farklı türdeki istisnaları temsil eden bir sınıf hiyerarşisi sağlar. Bu sınıflar içinde köklü java.langpaketin Throwableonun ile birlikte sınıfa Exception, RuntimeExceptionve Erroralt sınıfları.

Throwableistisnaların söz konusu olduğu nihai süper sınıftır. Yalnızca Throwableve alt sınıflarından oluşturulan nesneler atılabilir (ve daha sonra yakalanabilir). Bu tür nesneler fırlatılabilirler olarak bilinir .

Bir Throwablenesne, bir istisnayı tanımlayan bir ayrıntı mesajıyla ilişkilendirilir . Aşağıda açıklanan çift dahil olmak üzere birkaç kurucu, Throwablebir detay mesajı ile veya olmadan bir nesne oluşturmak için sağlanmıştır :

  • Throwable () , Throwableayrıntılı mesaj içermeyen bir mesaj oluşturur. Bu kurucu, bağlamın olmadığı durumlar için uygundur. Örneğin, yalnızca bir yığının boş veya dolu olduğunu bilmek istersiniz.
  • Throwable (String message) , detay mesajı olarak bir Throwablewith oluşturur message. Bu mesaj kullanıcıya gönderilebilir ve / veya günlüğe kaydedilebilir.

ThrowableString getMessage()detay mesajını döndürme yöntemini sağlar . Ayrıca, daha sonra tanıtacağım ek yararlı yöntemler de sağlar.

Exception sınıfı

Throwableiki direkt alt sınıfa sahiptir. Bu alt sınıflardan biri Exception, harici bir faktörden (var olmayan bir dosyadan okumaya çalışmak gibi) kaynaklanan bir istisnayı tanımlayan sınıftır. Exceptionile aynı yapıcıları (aynı parametre listelerine sahip) bildirir Throwableve her kurucu Throwablekarşılığını çağırır . metodlarını Exceptionmiras alır Throwable; yeni yöntem bildirmez.

Java, doğrudan alt sınıf olan birçok istisna sınıfı sağlar Exception. İşte üç örnek:

  • CloneNotSupportedException , sınıfı Cloneablearabirimi uygulamayan bir nesneyi klonlama girişimini işaret eder . Her iki tür de java.langpakette.
  • IOException , bir tür G / Ç arızasının meydana geldiğini gösterir. Bu tür java.iopakette bulunur.
  • ParseException , metin ayrıştırılırken bir hata oluştuğunu belirtir. Bu tür java.textpakette bulunabilir.

Her bir Exceptionalt sınıf adının kelime ile bittiğine dikkat edin Exception. Bu kural, sınıfın amacını belirlemeyi kolaylaştırır.

Genellikle Exceptionkendi istisna sınıflarınızla (adları ile bitmesi gereken) alt sınıflara (veya alt sınıflarından birine) sahip olursunuz Exception. İşte birkaç özel alt sınıf örneği:

public class StackFullException extends Exception { } public class EmptyDirectoryException extends Exception { private String directoryName; public EmptyDirectoryException(String message, String directoryName) { super(message); this.directoryName = directoryName; } public String getDirectoryName() { return directoryName; } }

İlk örnek, ayrıntılı bir mesaj gerektirmeyen bir istisna sınıfını açıklamaktadır. Varsayılan noargument yapıcısı çağırır Exception()ve çağırır Throwable().

İkinci örnek, yapıcısı bir ayrıntı mesajı ve boş dizinin adını gerektiren bir istisna sınıfını açıklar. Yapıcı Exception(String message), çağıran çağırır Throwable(String message).

ExceptionAlt sınıflarından veya alt sınıflarından birinden örneklenen nesneler (alt sınıflarından biri veya alt sınıfları hariç RuntimeException), istisnalar olarak kontrol edilir.

RuntimeException sınıfı

Exceptiondoğrudan RuntimeException, kötü yazılmış koddan kaynaklanan bir istisnayı açıklayan alt sınıflara ayrılır . RuntimeExceptionile aynı yapıcıları (aynı parametre listelerine sahip) bildirir Exceptionve her kurucu Exceptionkarşılığını çağırır . yöntemlerini RuntimeExceptionmiras alır Throwable. Yeni yöntem beyan etmez.

Java, doğrudan alt sınıf olan birçok istisna sınıfı sağlar RuntimeException. Aşağıdaki örnekler java.langpaketin tüm üyeleridir :

  • ArithmeticException , bir tamsayıyı 0'a bölmeye çalışmak gibi geçersiz bir aritmetik işlemi işaret eder.
  • IllegalArgumentException , bir yönteme geçersiz veya uygunsuz bir argümanın geçirildiğini gösterir.
  • NullPointerException, bir yöntemi çağırma veya boş başvuru aracılığıyla bir örnek alanına erişme girişimini işaret eder.

RuntimeExceptionAlt sınıflarından biri veya alt sınıflarından örneklenen nesneler , denetlenmemiş istisnalardır .

Error sınıfı

Throwable'nin diğer doğrudan alt sınıfı, Errorbellek yetersizliği, JVM yığınının taşması veya bulunamayan bir sınıfı yüklemeye çalışma gibi makul bir uygulamanın üstesinden gelmemesi gereken ciddi (hatta anormal) bir sorunu tanımlayan altsınıftır . Gibi Exception, Errorözdeş kurucuları bildirir Throwable, Throwableyöntemlerini miras alır ve kendi yöntemlerinden herhangi birini bildirmez.

ErrorAlt sınıfları, sınıf adlarıyla biten kuraldan tanımlayabilirsiniz Error. Örnekler arasında OutOfMemoryError, LinkageErrorve bulunur StackOverflowError. Her üç tip de java.langpakete aittir .

İstisnalar atmak

AC kütüphane işlevi, genel errnodeğişkeni bir hata koduna ayarlayarak ve bir hata kodu döndürerek bir istisnanın çağrılan kodunu bildirir . Buna karşılık, bir Java yöntemi bir nesneyi fırlatır. Özel durumların nasıl ve ne zaman atılacağını bilmek, etkili Java programlamanın önemli bir yönüdür. Bir istisna atmak iki temel adımı içerir:

  1. throwBir istisna nesnesi atmak için ifadeyi kullanın .
  2. throwsDerleyiciyi bilgilendirmek için tümceyi kullanın .

Sonraki bölümler istisnaları yakalamaya ve onlardan sonra temizlemeye odaklanacak, ancak önce fırlatılabilir ürünler hakkında daha fazla bilgi edinelim.

Fırlatma ifadesi

Java, throwbir istisnayı tanımlayan bir nesneyi atma ifadesini sağlar . İfadenin sözdizimi throwşöyledir:

throw throwable;

The object identified by throwable is an instance of Throwable or any of its subclasses. However, you usually only throw objects instantiated from subclasses of Exception or RuntimeException. Here are a couple of examples:

throw new FileNotFoundException("unable to find file " + filename); throw new IllegalArgumentException("argument passed to count is less than zero");

The throwable is thrown from the current method to the JVM, which checks this method for a suitable handler. If not found, the JVM unwinds the method-call stack, looking for the closest calling method that can handle the exception described by the throwable. If it finds this method, it passes the throwable to the method's handler, whose code is executed to handle the exception. If no method is found to handle the exception, the JVM terminates with a suitable message.

The throws clause

You need to inform the compiler when you throw a checked exception out of a method. Do this by appending a throws clause to the method's header. This clause has the following syntax:

throws checkedExceptionClassName (, checkedExceptionClassName)*

A throws clause consists of keyword throws followed by a comma-separated list of the class names of checked exceptions thrown out of the method. Here is an example:

public static void main(String[] args) throws ClassNotFoundException { if (args.length != 1) { System.err.println("usage: java ... classfile"); return; } Class.forName(args[0]); }

This example attempts to load a classfile identified by a command-line argument. If Class.forName() cannot find the classfile, it throws a java.lang.ClassNotFoundException object, which is a checked exception.

Checked exception controversy

The throws clause and checked exceptions are controversial. Many developers hate being forced to specify throws or handle the checked exception(s). Learn more about this from my Are checked exceptions good or bad? blog post.