अंत में एक अपवाद फेंकना


27

जब कोई अपवाद किसी finallyब्लॉक के अंदर फेंका जा सकता है, तो फोर्टीज "शिकायत" जैसे स्टेटिक कोड एनालाइजर का विश्लेषण करते हैं Using a throw statement inside a finally block breaks the logical progression through the try-catch-finally। आम तौर पर मैं इससे सहमत हूं। लेकिन हाल ही में मैं इस कोड में आया हूँ:

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

अब अगर writerठीक से बंद नहीं किया जा सकता है, तो writer.close()विधि एक अपवाद फेंक देगी। एक अपवाद फेंक दिया जाना चाहिए क्योंकि (शायद सबसे) फ़ाइल को लिखने के बाद सहेजा नहीं गया था।

मैं एक अतिरिक्त चर की घोषणा कर सकता था, इसे सेट कर सकता था यदि writerअंत में ब्लॉक करने के बाद एक अपवाद को बंद करने और फेंकने में कोई त्रुटि थी । लेकिन यह कोड ठीक काम करता है और मुझे यकीन नहीं है कि इसे बदलना है या नहीं।

finallyब्लॉक के अंदर एक अपवाद फेंकने की कमियां क्या हैं ?


4
यदि यह जावा है, और आप जावा 7 का उपयोग कर सकते हैं, तो जांचें कि क्या एआरएम ब्लॉक आपकी समस्या को हल कर सकते हैं।
लांडेई

@ लेन्डी, यह इसे हल करता है, लेकिन दुर्भाग्य से हम जावा 7.
सुपरम

मैं कहूंगा कि आपने जो कोड दिखाया है, वह "अंत में ब्लॉक के अंदर फेंक स्टेटमेंट का उपयोग करना" नहीं है और जैसे कि तार्किक प्रगति ठीक है।
माइक

@ माइक, मैंने मानक सारांश का उपयोग किया है जो फोर्टिफाई दिखाता है, लेकिन प्रत्यक्ष या अप्रत्यक्ष रूप से अंत में फेंक दिया गया अपवाद है।
सुपर

दुर्भाग्य से, प्रयास-के साथ-साथ संसाधन ब्लॉक को फोर्टिफ़ द्वारा अंत में फेंके गए अपवाद के रूप में भी पहचाना जाता है .. यह बहुत स्मार्ट है, धिक्कार है .. फिर भी यह सुनिश्चित नहीं है कि इसे कैसे पार किया जाए, यह कोशिश-संसाधनों के कारण बंद करने का आश्वासन देना था। अंत में संसाधन, और अब इस तरह के प्रत्येक बयान को
फोर्टीज

जवाबों:


18

मूल रूप से, finallyएक संसाधन के समुचित रिलीज को सुनिश्चित करने के लिए खंड हैं। हालाँकि, यदि कोई अपवाद अंततः ब्लॉक के अंदर फेंका जाता है, तो वह गारंटी समाप्त हो जाती है। इससे भी बदतर, अगर आपके कोड का मुख्य ब्लॉक अपवाद फेंकता है, तो finallyब्लॉक में उठाया गया अपवाद इसे छिपा देगा। ऐसा लगेगा कि कॉल के कारण त्रुटि हुई थी close, वास्तविक कारण से नहीं।

कुछ लोग नेस्टेड अपवाद हैंडलर के एक खराब पैटर्न का पालन करते हैं, जो finallyब्लॉक में फेंके गए अपवादों को निगलते हैं ।

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

जावा के पुराने संस्करणों में, आप कक्षाओं में संसाधनों को लपेटकर इस कोड को "सरल" कर सकते हैं जो आपके लिए यह "सुरक्षित" सफाई करते हैं। मेरा एक अच्छा दोस्त अनाम प्रकारों की एक सूची बनाता है, प्रत्येक जो अपने संसाधनों की सफाई के लिए तर्क प्रदान करता है। फिर उसका कोड सूची पर लूप हो जाता है और finallyब्लॉक के भीतर डिस्पोज़ विधि को कॉल करता है ।


1
+1 हालांकि मैं उन लोगों में शामिल हूं जो उस गंदा कोड का उपयोग करते हैं। लेकिन मेरे औचित्य के लिए मैं कहूंगा कि मैं हमेशा ऐसा नहीं करता, केवल जब अपवाद महत्वपूर्ण नहीं होता है।
सुपर

2
मैं खुद को ऐसा करते हुए पाता हूं। किसी भी समय पूरे संसाधन प्रबंधक (अनाम या नहीं) कोड के उद्देश्य से अलग हो जाते हैं।
ट्रैविस पार्क

मुझसे भी +1; आपने मूल रूप से ठीक वही कहा था जो मैं बेहतर करने जा रहा था। ध्यान देने योग्य बात यह है कि जावा में कुछ स्ट्रीम कार्यान्वयन वास्तव में अपवादों को बंद नहीं कर सकते हैं (), लेकिन इंटरफ़ेस इसे घोषित करता है क्योंकि उनमें से कुछ करते हैं। इसलिए, कुछ परिस्थितियों में आप एक कैच ब्लॉक जोड़ सकते हैं, जिसकी वास्तव में आवश्यकता नहीं होगी।
विघ्नह्रदय

1
अंत में ब्लॉक के भीतर एक कोशिश / पकड़ने ब्लॉक का उपयोग करने के बारे में इतना बुरा क्या है? मैं ऐसा नहीं करूंगा कि मेरे सभी कनेक्शन और स्ट्रीम आदि को लपेटने के लिए मेरे सभी कोड ब्लोट की तुलना में, ताकि वे चुपचाप बंद हो सकें - जो, अपने आप में, कनेक्शन या स्ट्रीम को बंद करने के लिए नहीं जानने की एक नई समस्या का परिचय देता है ( या जो भी) एक अपवाद का कारण बनता है - जो कुछ ऐसा है जिसके बारे में आप वास्तव में जानना चाहते हैं या इसके बारे में कुछ करना चाहते हैं ...
प्रतिबंध-जियोइंजीनियरिंग

10

ट्रैविस पार्क्स ने जो कहा वह सच है कि finallyब्लॉक में अपवाद किसी भी रिटर्न मान या try...catchब्लॉक से अपवादों का उपभोग करेंगे ।

यदि आप जावा 7 का उपयोग कर रहे हैं, हालांकि, एक कोशिश के साथ संसाधन ब्लॉक का उपयोग करके समस्या को हल किया जा सकता है । डॉक्स के अनुसार, जब तक आपके संसाधन लागू होते हैं java.lang.AutoCloseable(अधिकांश पुस्तकालय लेखक / पाठक अब करते हैं), कोशिश-के साथ संसाधन ब्लॉक इसे आपके लिए बंद कर देगा। यहां अतिरिक्त लाभ यह है कि इसे बंद करते समय होने वाला कोई भी अपवाद दबा दिया जाएगा, जिससे मूल वापसी मूल्य या अपवाद को पारित करने की अनुमति मिलती है।

से

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
}

सेवा मेरे

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
यह कभी-कभी मददगार होता है। लेकिन मामले में जब मुझे वास्तव में अपवाद को फेंकने की आवश्यकता होती है जब फ़ाइल बंद नहीं हो सकती है, तो यह दृष्टिकोण समस्याओं को छिपाता है।
सुपरम

1
@superM वास्तव में, कोशिश-के साथ संसाधनों से एक अपवाद छिपा नहीं है close()। "कोई भी अपवाद जो इसे बंद करते समय होता है, को दबा दिया जाएगा, मूल रिटर्न मान या पास अप करने की अनुमति देता है" - यह बस सच नहीं हैclose()विधि से एक अपवाद केवल तभी दबाया जाएगा जब दूसरा अपवाद कोशिश / कैच ब्लॉक से फेंका जाएगा। इसलिए, यदि tryब्लॉक कोई अपवाद नहीं फेंकता है, तो close()विधि से अपवाद को फेंक दिया जाएगा (और वापसी मूल्य वापस नहीं किया जाएगा)। यहां तक ​​कि इसे वर्तमान catchब्लॉक से भी पकड़ा जा सकता है ।
रुस्लान

0

मुझे लगता है कि यह कुछ ऐसा है जिसे आपको केस के आधार पर किसी मामले पर संबोधित करने की आवश्यकता होगी। कुछ मामलों में विश्लेषक जो कह रहा है वह सही है कि आपके पास जो कोड है वह महान नहीं है और पुनर्विचार की आवश्यकता है। लेकिन फेंकने या फिर से फेंकने पर अन्य मामले भी हो सकते हैं जो सबसे अच्छी बात हो सकती है। यह ऐसा कुछ नहीं है जिसे आप जनादेश दे सकते हैं।


0

यह एक वैचारिक उत्तर देने वाला है कि ये चेतावनियाँ संसाधनों के साथ-साथ दृष्टिकोण के साथ भी क्यों मौजूद हो सकती हैं। यह दुर्भाग्य से आसान समाधान नहीं है जिसे आप प्राप्त करने की उम्मीद कर रहे हैं।

त्रुटि पुनर्प्राप्ति विफल नहीं हो सकती

finally लेन-देन के सफल होने या विफल होने के बावजूद निष्पादित किए जाने वाले नियंत्रण के बाद के प्रवाह को मॉडल करता है।

विफलता के मामले में, एक त्रुटि से उबरने finallyके बीच में निष्पादित तर्क को पकड़ लेता है , इससे पहले कि यह पूरी तरह से पुनर्प्राप्त हो जाए (इससे पहले कि हम अपने catchगंतव्य तक पहुंचें )।

एक त्रुटि से उबरने के बीच में एक त्रुटि का सामना करने के लिए प्रस्तुत वैचारिक समस्या की कल्पना करें ।

एक डेटाबेस सर्वर की कल्पना करें जहां हम लेनदेन करने की कोशिश कर रहे हैं, और यह आधे रास्ते में विफल रहता है (जैसे कि सर्वर बीच में मेमोरी से बाहर भाग गया)। अब सर्वर लेन-देन को वापस एक बिंदु पर ले जाना चाहता है, हालांकि ऐसा कुछ नहीं हुआ। फिर भी कल्पना कीजिए कि यह वापस रोलिंग की प्रक्रिया में एक और त्रुटि है। अब हम डेटाबेस के लिए एक आधा प्रतिबद्ध लेनदेन कर रहे हैं - परमाणुता और लेनदेन की अविभाज्य प्रकृति अब टूट गई है, और डेटाबेस की अखंडता से अब समझौता किया जाएगा।

यह वैचारिक समस्या किसी भी भाषा में मौजूद है जो त्रुटियों से निपटती है चाहे वह सी त्रुटि मैनुअल प्रचार के साथ हो, या अपवादों और विनाशकों के साथ सी ++, या अपवादों के साथ जावा finally

finally भाषाओं में विफल नहीं हो सकता है जो इसे उसी तरह प्रदान करते हैं, जो विध्वंसक अपवादों का सामना करने की प्रक्रिया में C ++ में विफल नहीं हो सकते।

इस वैचारिक और कठिन समस्या से बचने का एकमात्र तरीका यह सुनिश्चित करना है कि लेनदेन को वापस लाने और बीच में संसाधनों को जारी करने की प्रक्रिया संभवतः एक पुनरावर्ती अपवाद / त्रुटि का सामना नहीं कर सकती है।

तो यहाँ केवल सुरक्षित डिज़ाइन एक ऐसा डिज़ाइन है जहाँ writer.close()संभवतः विफल नहीं हो सकता। आम तौर पर परिदृश्यों से बचने के लिए डिजाइन में ऐसे तरीके हैं जहां ऐसी चीजें वसूली के बीच में विफल हो सकती हैं, जिससे यह असंभव है।

यह दुर्भाग्य से एकमात्र तरीका है - त्रुटि पुनर्प्राप्ति विफल नहीं हो सकती। यह सुनिश्चित करने का सबसे आसान तरीका है कि उन प्रकार के "संसाधन जारी करना" और "रिवर्स साइड इफेक्ट्स" कार्य को विफल करने में असमर्थ बनाया जाए। यह आसान नहीं है - उचित त्रुटि वसूली कठिन है और दुर्भाग्य से परीक्षण करना भी कठिन है। लेकिन इसे प्राप्त करने का तरीका यह सुनिश्चित करना है कि कोई भी फ़ंक्शन जो "नष्ट", "बंद", "शटडाउन", "रोल बैक", आदि प्रक्रिया में एक बाहरी त्रुटि का सामना नहीं कर सकता है, क्योंकि ऐसे कार्यों को अक्सर आवश्यकता होगी। मौजूदा त्रुटि से उबरने के बीच में कहा जाता है।

उदाहरण: लॉगिंग

मान लें कि आप किसी finallyब्लॉक के अंदर सामान लॉग इन करना चाहते हैं । यह अक्सर एक बड़ी समस्या होने वाली है जब तक कि लॉगिंग विफल नहीं हो सकती । लॉगिंग लगभग निश्चित रूप से विफल हो सकती है, क्योंकि यह फाइल करने के लिए अधिक डेटा संलग्न करना चाह सकता है, और वह आसानी से विफल होने के कई कारण पा सकता है।

तो यहाँ समाधान यह है कि इसे बनाने के लिए finallyब्लॉक में इस्तेमाल होने वाला कोई भी लॉगिंग फ़ंक्शन कॉलर को नहीं फेंक सकता है (यह विफल हो सकता है, लेकिन यह फेंक नहीं पाएगा)। हम ऐसा कैसे कर सकते हैं? यदि आपकी भाषा अंत में प्रदान किए गए संदर्भ के भीतर फेंकने की अनुमति देती है, तो एक नेस्टेड कोशिश / कैच ब्लॉक है, जो अपवादों को निगलने और उन्हें त्रुटि कोड में बदलकर कॉलर को फेंकने से बचने का एक तरीका होगा, उदाहरण के लिए, लॉगिंग को एक अलग तरीके से किया जा सकता है। प्रक्रिया या धागा जो एक मौजूदा त्रुटि-रिकवरी स्टैक के अलग और बाहर विफल हो सकते हैं। जब तक आप एक त्रुटि में चलने की संभावना के बिना उस प्रक्रिया के साथ संवाद कर सकते हैं, वह भी अपवाद-सुरक्षित होगा, क्योंकि सुरक्षा मुद्दा केवल इस परिदृश्य में मौजूद है अगर हम उसी धागे के भीतर से पुनरावृत्ति कर रहे हैं।

इस मामले में, हम लॉगिंग की विफलता के साथ दूर हो सकते हैं बशर्ते कि यह लॉग इन करने में विफल न हो और कुछ भी नहीं करना दुनिया का अंत नहीं है (यह किसी भी संसाधन को लीक नहीं कर रहा है या बैक साइड इफेक्ट्स को रोल करने में विफल नहीं है, जैसे)।

वैसे भी, मुझे यकीन है कि आप पहले से ही कल्पना करना शुरू कर सकते हैं कि सॉफ्टवेयर अपवाद को सुरक्षित बनाना वास्तव में कितना मुश्किल है। यह सभी के लिए पूर्ण डिग्री की तलाश करने के लिए आवश्यक नहीं हो सकता है, लेकिन सबसे मिशन-महत्वपूर्ण सॉफ़्टवेयर। लेकिन यह ध्यान देने योग्य है कि वास्तव में अपवाद-सुरक्षा कैसे प्राप्त की जा सकती है, क्योंकि बहुत सामान्य-उद्देश्य वाले पुस्तकालय लेखक भी अक्सर यहां फैंकते हैं और पुस्तकालय का उपयोग करते हुए आपके आवेदन के पूरे अपवाद-सुरक्षा को बर्बाद कर देते हैं।

SomeFileWriter

यदि SomeFileWriterआप अंदर फेंक सकते हैं close, तो मैं कहूंगा कि यह आम तौर पर अपवाद-हैंडलिंग से असंगत है, जब तक कि आप इसे एक ऐसे संदर्भ में बंद करने की कोशिश नहीं करते हैं जिसमें मौजूदा अपवाद से उबरना शामिल है। यदि इसके लिए कोड आपके नियंत्रण से बाहर है, तो हम SOL हो सकते हैं, लेकिन यह इस स्पष्ट अपवाद-सुरक्षा मुद्दे के लेखकों को सूचित करने के लायक होगा। यदि यह आपके नियंत्रण में है, तो मेरी मुख्य सिफारिश यह सुनिश्चित करना है कि इसे बंद करना संभवतया जो भी आवश्यक हो, विफल हो सकता है।

कल्पना करें कि क्या कोई ऑपरेटिंग सिस्टम वास्तव में किसी फ़ाइल को बंद करने में विफल हो सकता है। अब कोई भी प्रोग्राम जो शट डाउन पर किसी फ़ाइल को बंद करने की कोशिश करता है, वह शट डाउन करने में विफल हो जाएगा । अब हम क्या करने वाले हैं, बस आवेदन को खुला और सीमित रखें (शायद नहीं), बस फ़ाइल संसाधन को लीक करें और समस्या को अनदेखा करें (शायद ठीक है अगर यह इतना महत्वपूर्ण नहीं है)? सबसे सुरक्षित डिज़ाइन: इसे बनाएं ताकि किसी फ़ाइल को बंद करने में विफल होना असंभव हो।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.