Sonunda bir istisna atmak


27

Fortify gibi statik kod analizörleri bir finallybloğun içine bir istisna atılabileceğini söyleyerek "şikayet" eder Using a throw statement inside a finally block breaks the logical progression through the try-catch-finally. Normalde buna katılıyorum. Ancak son zamanlarda bu koda rastladım:

SomeFileWriter writer = null; 
try { 
     //init the writer
     //write into the file
} catch (...) {
     //exception handling
} finally {
     if (writer!= null) writer.close();  
}

Şimdi eğer writeruygun şekilde kapatılamazsa, writer.close()yöntem bir istisna atar. Bir istisna atılmalıdır, çünkü (büyük olasılıkla) dosya yazıldıktan sonra kaydedilmedi.

Fazladan bir değişken ilan edebilir, kapanışta bir hata olursa onu ayarlayabilir writerve sonunda bloktan sonra bir istisna atabilirim. Ama bu kod iyi çalışıyor ve değiştirip değiştirmeyeceğinden emin değilim.

finallyBloğun içine bir istisna atmanın sakıncaları nelerdir ?


4
Eğer bu bir Java ise ve Java 7'yi kullanabilirsiniz, ARM bloklarının probleminizi çözüp çözemediğini kontrol edin.
Landei

@Landei, bu çözer onu, ama ne yazık ki kullanmıyorsanız Java 7.
Superm

Göstermiş olduğunuz kodun "bir nihayet bloğun içinde bir throw ifadesi kullanmak" olmadığını ve mantıksal ilerlemenin iyi olduğunu söyleyebilirim.
Mike

@Mike, Fortify'ın gösterdiği standart özeti kullandım, ancak doğrudan veya dolaylı olarak nihayet içine atılan bir istisna var.
13.01.

Ne yazık ki, kaynaklar ile deneyin bloğu aynı zamanda Fortify tarafından nihayet içine atılan istisna olarak da algılandı. Çok akıllı, lanet .. hala bunun nasıl üstesinden geleceğinden emin değil, kaynakları denemenin sebebi, kaynakların kapatılmasını sağlamaktı. Kaynaklar nihayet, ve şimdi böyle bir açıklama Fortify tarafından bir güvenlik tehdidi olarak rapor edildi ..
mmona

Yanıtlar:


18

Temel olarak, finallybir kaynağın uygun şekilde salıverilmesini sağlamak için maddeler var. Ancak, nihayet bloğun içine bir istisna atılırsa, bu garanti ortadan kalkar. Daha da kötüsü, ana kod bloğunuz bir istisna atarsa, finallyblokta belirtilen istisna bunu gizler. Hatanın closesebebi, gerçek nedenden dolayı yapılan çağrıdan kaynaklanıyor gibi görünecektir .

Bazı insanlar, finallyblokta atılan istisnaları yutarak, iç içe geçmiş istisna eylemcileri içeren kötü bir kalıp izler .

SomeFileWriter writer = null; 
try { 
     //init the writer
     //write into the file
} finally {
    if (writer!= null) {
        try {
            writer.close();
        } catch (...) {
        }
    }
}

Java'nın eski sürümlerinde, bu "güvenli" işlemi yapan sınıfları kaynakları sizin için kaplayarak bu kodu "basitleştirebilirsiniz". İyi bir arkadaşım, her biri kaynaklarını temizlemenin mantığını sağlayan adsız türlerin bir listesini oluşturur. Sonra onun kodu basitçe listede dolaşıyor ve finallyblok içindeki dispose yöntemini çağırıyor .


1
+1 Bu pis kodu kullananlar arasındayım. Ancak gerekçeme göre, bunu sadece istisna kritik olmadığında her zaman yapmadığımı söyleyeceğim.
SuperM

2
Kendimi de yaparken buluyorum. Bazı zamanlarda herhangi bir kaynak yöneticisinin tamamını (anonim veya değil) oluşturmak, kodun amacından sapar.
Travis Parks

Benden de +1; temelde tam olarak ne yapacağımı ama daha iyi olduğumu söyledin. Dikkat edilmesi gereken nokta, Java'daki Stream uygulamalarının bazılarının aslında Exceptions'ı close () üzerine koyamamasıdır, ancak bazılarının yaptıkları için arabirim bunu açıklar. Bu nedenle, bazı durumlarda, hiçbir zaman ihtiyaç duyulmayacak bir toplama bloğu ekliyor olabilirsiniz.
vaughandroid

1
Bir nihayet bloğun içinde bir try / catch bloğu kullanmanın nesi kötü? Bunu, tüm bağlantılarımı ve akışlarımı, vb. Sararmak zorunda kalmadan tüm kodumu kırmak yerine, sessizce kapatılabilmeleri için kodumu şişirmeyi tercih ederim. ya da her neyse) bir istisna yaratır - ki bu aslında bilmek ya da hakkında bir şeyler yapmaktan
hoşlanabileceğiniz bir

10

Travis Parks'ın söylediği şey, finallybloktaki istisnaların blok değerlerine veya istisnaları try...catchbloklara göre tüketeceğidir .

Yine de Java 7 kullanıyorsanız, problem bir kaynaklar ile deneyin bloğu kullanılarak çözülebilir . Belgelere göre, kaynağınız uygulandığı sürece java.lang.AutoCloseable(çoğu kütüphane yazarının / okuyucusunun yaptığı şimdi), kaynakları denemek bloğu sizin için kapanacaktır. Buradaki ek yarar, kapanırken meydana gelen herhangi bir istisnanın, orijinal iade değerinin veya istisnanın artmasına izin verecek şekilde bastırılmasıdır.

itibaren

FileWriter writer = null;
try {
  writer = new FileWriter("myFile.txt");
  writer.write("hello");
} catch(...) {
  // return/throw new exception
} finally {
  writer.close(); // an exception would consume the catch block's return/exception
}

için

try (FileWriter writer = new FileWriter("myFile.txt")) {
  writer.write("hello");
} catch(...) {
  // return/throw new exception, always gets returned even if writer fails to close
}

http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html


1
Bu bazen yardımcı olur. Ancak, dosya kapatılamadığında bir istisna atmam gerektiğinde, bu yaklaşım sorunları gizler.
sabah 13

1
@superM Aslında, kaynakları denemek bir istisna oluşturmaz close(). "Kapatırken meydana gelen herhangi bir istisna, orijinal geri dönüş değerinin veya istisnanın geçmesine izin vererek bastırılacak" - bu sadece doğru değil . close()Yöntemden bir istisna, yalnızca try / catch bloğundan başka bir istisna atıldığında ortaya çıkar. Bu nedenle, tryblok herhangi bir istisna atmazsa, close()yöntemin istisnası atılır (ve dönüş değeri döndürülmez). Mevcut catchbloktan bile yakalanabilir .
Ruslan Stelmachenko

0

Sanırım bu, duruma göre ele almanız gereken bir şey. Bazı durumlarda analizörün söylediği şey doğru, sahip olduğunuz kodun çok iyi olmaması ve yeniden düşünmeye ihtiyacı olması. Ancak fırlatma veya yeniden fırlatma bile en iyi şey olabilir, başka durumlar olabilir. Zorlayabileceğiniz bir şey değil.


0

Bu, kaynakların denenmesi yaklaşımlarında bile bu uyarıların neden olabileceğine dair daha kavramsal bir cevap olacaktır. Aynı zamanda maalesef, elde etmeyi umduğunuz kolay bir çözüm değildir.

Hata Kurtarma Başarısız Olamıyor

finally Bir işlemin başarılı veya başarısız olmasına bakılmaksızın yürütülen işlem sonrası kontrol akışını modeller.

Başarısızlık durumunda, bir hatanın giderilmesinin ortasında , tamamen kurtarılmadan önce (hedefimize ulaşmadan önce ) finallyyürütülen mantığı yakalar .catch

Bir hatanın giderilmesinin ortasında bir hatayla karşılaşmak için sunduğu kavramsal sorunu düşünün .

Bir işlem yapmaya çalıştığımız bir veritabanı sunucusunu hayal edin ve yarı yolda başarısız olur (sunucunun hafızasının ortasında kaldığını söyleyin). Artık sunucu, hiçbir şey olmamış gibi işlemi geri almak istiyor. Yine de geri dönme sürecinde başka bir hatayla karşılaştığını hayal edin. Şimdi veritabanına yarı-bağlı bir işlem yaptık - işlemin atomitesi ve bölünmez doğası bozuldu ve veritabanının bütünlüğü artık tehlikeye girecek.

Bu kavramsal sorun, manuel hata kodu yayılımı ile C veya istisnalar ve yıkıcılar ile C ++ veya istisnalar dışında Java ile ilgili hatalarla ilgilenen herhangi bir dilde mevcuttur finally.

finally aynı şekilde sağlayan dillerde başarısız olamaz, yıkıcılar istisnalarla karşılaşma sürecinde C ++ 'da başarısız olamazlar.

Bu kavramsal ve zor problemden kaçınmanın tek yolu, işlemleri geri alma ve kaynakları ortada bırakma sürecinin muhtemelen özyinelemeli bir istisna / hata ile karşı karşıya kalmayacağından emin olmaktır.

Yani buradaki tek güvenli tasarım, writer.close()başarısız olamayacak bir tasarım . Tasarımda, bu tür şeylerin iyileşme ortasında başarısız olabileceği senaryolardan kaçınmanın imkansız hale getirilmesi için genellikle yollar vardır.

Maalesef bu tek yol - hata kurtarma başarısız olamaz. Bunu sağlamanın en kolay yolu, bu tür "kaynakların serbest bırakılması" ve "ters yan etkiler" işlevlerinin başarısızlığa uğramaz hale getirmektir. Kolay değil - uygun hata kurtarma işlemi zor ve ne yazık ki test edilmesi de zor. Ancak bunu başarmanın yolu, "yok", "kapat", "kapat", "geri al" vb. İşlevlerin, işlem sırasında harici bir hatayla karşılaşmayacağından emin olmaktır; Mevcut bir hatadan kurtarma ortasında çağrılabilir.

Örnek: Günlüğe kaydetme

Diyelim ki bir finallybloğun içine bir şeyler kaydetmek istiyorsunuz Bu, günlüğe kaydetme başarısız olmadıkça büyük bir sorun olacaktır . Günlük kaydı neredeyse kesin olarak başarısız olabilir, çünkü dosyaya daha fazla veri eklemek isteyebilir ve bu da başarısızlığın birçok nedenini kolayca bulabilir.

Dolayısıyla buradaki çözüm, finallybloklarda kullanılan herhangi bir kayıt işlevinin arayan kişiye atamayacağı şekilde yapmaktır (başarısız olabilir, ancak atmaz). Bunu nasıl yapabiliriz? Diliniz nihayetinde yuvalanmış bir try / catch bloğu olması koşuluyla atmaya izin veriyorsa, bu, istisnaları yutarak ve onları hata kodlarına dönüştürerek arayana atmaktan kaçınmanın bir yolu olabilir; Ayrı olarak ve mevcut bir hata kurtarma yığınının dışında kalan bir süreç veya iş parçacığı gevşer. Bir hatayla karşılaşmanız mümkün olmadan bu işlemle iletişim kurabildiğiniz sürece, bu aynı zamanda istisnai güvenlik de olabilir, çünkü güvenlik konusu sadece aynı konu içinden tekrar tekrar atıyorsak bu senaryoda ortaya çıkar.

Bu durumda, giriş yapmamak ve hiçbir şey yapmamak dünyanın sonu olmadığından fırlatmamak kaydıyla kayıt başarısızlığından kurtulabiliriz (örneğin, herhangi bir kaynağı sızdırmaz veya yan etkileri geri almayı başaramaz).

Her neyse, bir yazılımı gerçekten de istisnai güvenli hale getirmenin ne kadar zor olduğunu hayal etmeye başlayacağınıza eminim. Görev açısından en kritik yazılımdan başka bir şeyle bunu en üst düzeye çıkarmak gerekli olmayabilir. Ancak, istisnai güvenliğin nasıl sağlanacağına dikkat çekmek önemlidir; çünkü çok genel amaçlı kütüphane yazarları burada sık sık karıştırabilir ve kütüphane kullanarak uygulamanızın istisna güvenliğini tamamen bozabilir.

SomeFileWriter

Eğer SomeFileWriteriçine atabilirseniz close, o zaman, mevcut bir istisnadan kurtarmayı içeren bir bağlamda asla kapatmaya çalışmadığınız sürece, istisna işleme ile genellikle uyumsuz olduğunu söyleyebilirim. Eğer onun kodu sizin kontrolünüz dışındaysa, SOL olabiliriz ancak bu açık istisna güvenlik konusunun yazarlarına bildirimde bulunmaya değer. Eğer sizin kontrolünüz dahilindeyse, temel tavsiyem, gerekli olan herhangi bir şekilde kapatmanın mümkün olamayacağından emin olmak.

Bir işletim sisteminin bir dosyayı gerçekten kapatmayı başaramadığını hayal edin. Artık kapatıldığında bir dosyayı kapatmaya çalışan herhangi bir program kapatılamaz . Şimdi ne yapmamız gerekiyor, sadece uygulamayı açık ve açık tutmaya devam etmemiz (muhtemelen hayır), dosya kaynağını sızdırmanız ve sorunu görmezden gelmeniz (çok kritik değilse tamam olabilir)? En güvenli tasarım: bir dosyayı kapatmayı başaramamak imkansız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.