स्थैतिक आरंभीकरण ब्लॉक से जावा अपवाद की जाँच करने की अनुमति क्यों नहीं देता है?


135

जावा स्थैतिक आरंभीकरण खंड से जाँच अपवाद को फेंकने की अनुमति क्यों नहीं देता है? इस डिजाइन निर्णय के पीछे क्या कारण था?


स्टैटिक ब्लॉक में आप किस तरह का अपवाद फेंकना चाहते हैं?
काई हुप्पमन

1
मैं ऐसा कुछ नहीं करना चाहता हूं। मैं सिर्फ यह जानना चाहता हूं कि स्टैटिक ब्लॉक के अंदर चेक किए गए अपवादों को पकड़ना क्यों अनिवार्य है।
लापताफैक्टर

फिर आप एक अपवादित अपवाद की उम्मीद कैसे करेंगे? यदि यह आपको परेशान करता है, तो केवल नए रनटाइम एक्सेप्शन ("संदेश कह रहा है", ई) के साथ पकड़े गए अपवाद को हटा दें;
थोरबजोरन राव एंडरसन

18
@ ThorbjørnRavnAndersen Java वास्तव में उस स्थिति के लिए एक अपवाद प्रकार प्रदान करता है: docs.oracle.com/javase/6/docs/api/java/lang/…
smp7x

@ smp7d नीचे केविनर्प जवाब देखें, और स्टीफन से इसकी टिप्पणी। यह एक बहुत अच्छी सुविधा है, लेकिन इसमें जाल हैं!
बेंज

जवाबों:


122

क्योंकि आपके स्रोत में इन चेक किए गए अपवादों को संभालना संभव नहीं है। आरंभीकरण प्रक्रिया पर आपका कोई नियंत्रण नहीं है और स्थैतिक {} ब्लॉक को आपके स्रोत से नहीं बुलाया जा सकता है ताकि आप उन्हें ट्राइ-कैच से घेर सकें।

चूँकि आप चेक किए गए अपवाद द्वारा बताई गई किसी भी त्रुटि को संभाल नहीं सकते हैं, इसलिए यह जाँच की गई कि अपवाद स्थैतिक ब्लॉक को फेंकना अस्वीकार कर दिया जाए।

स्टैटिक ब्लॉक को चेक किए गए अपवादों को नहीं फेंकना चाहिए, लेकिन फिर भी अनियंत्रित / रनटाइम-अपवादों को फेंकने की अनुमति देता है। लेकिन उपरोक्त कारणों के अनुसार आप इन्हें संभाल नहीं पाएंगे।

संक्षेप में, यह प्रतिबंध डेवलपर को कुछ बनाने से रोकता है (या कम से कम इसके लिए कठिन बनाता है) जिसके परिणामस्वरूप ऐसी त्रुटियां हो सकती हैं जिनसे आवेदन पुनर्प्राप्त करने में असमर्थ होगा।


69
दरअसल, यह जवाब गलत है। आप एक स्थिर ब्लॉक में अपवाद फेंक सकते हैं। आप जो नहीं कर सकते हैं वह है कि एक स्थिर ब्लॉक से बाहर प्रचारित करने के लिए एक चेक किए गए अपवाद की अनुमति दें ।
स्टीफन सी

16
आप इस अपवाद को संभाल सकते हैं, यदि आप Class.forName (..., true, ...) के साथ गतिशील क्लास लोडिंग कर रहे हैं; दी, यह कुछ ऐसा नहीं है जो आप बहुत बार करते हैं।
लेडीकैलेन

2
स्थैतिक {थ्रो न्यू NullPointerExcpetion ()} - यह भी संकलन नहीं करेगा!
किरिल बजावरो

4
@KirillBazarov एक ऐसा वर्ग है जिसमें हमेशा स्टैटिक इनिशियलाइज़र होता है जो हमेशा एक अपवाद के रूप में परिणत होता है (क्योंकि ऐसा क्यों होना चाहिए?) संकलन नहीं होगा। एक-खंड में उस थ्रो स्टेटमेंट को लपेटें और आप जाने के लिए अच्छे हैं।
कल्जा

2
@ रवीशा क्योंकि उस मामले में इनिशियलाइज़र के पास किसी भी मामले में सामान्य रूप से पूरा करने का कोई मौका नहीं है। ट्राइ-कैच के साथ Println द्वारा फेंका गया कोई अपवाद नहीं हो सकता है और इसलिए इनिशलाइज़र के पास बिना किसी अपवाद के पूरा होने का मौका है। यह एक अपवाद का बिना शर्त परिणाम है जो इसे एक संकलन-त्रुटि बनाता है। इसके लिए JLS देखें: docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.7 लेकिन कंपाइलर को आपके मामले में एक साधारण स्थिति जोड़कर मूर्ख बनाया जा सकता हैstatic { if(1 < 10) { throw new NullPointerException(); } }
कोसी 2801

67

आप किसी भी चेक किए गए अपवाद को पकड़कर और इसे अनियंत्रित अपवाद मानकर समस्या पर काम कर सकते हैं। यह अनियंत्रित अपवाद वर्ग एक आवरण के रूप में अच्छी तरह से काम करता है java.lang.ExceptionInInitializerError:।

नमूना कोड:

protected static class _YieldCurveConfigHelperSingleton {

    public static YieldCurveConfigHelper _staticInstance;

    static {
        try {
            _staticInstance = new YieldCurveConfigHelper();
        }
        catch (IOException | SAXException | JAXBException e) {
            throw new ExceptionInInitializerError(e);
        }
    }
}

1
@DK: शायद जावा का आपका संस्करण इस प्रकार के कैच क्लाज का समर्थन नहीं करता है। catch (Exception e) {इसके बजाय कोशिश करें ।
केविनपारे

4
हां, आप ऐसा कर सकते हैं, लेकिन यह वास्तव में बुरा विचार है। अनियंत्रित अपवाद वर्ग और किसी भी अन्य वर्गों को रखता है जो इस पर निर्भर करते हैं कि यह विफल राज्य है जो केवल कक्षाओं को उतारकर हल किया जा सकता है। यह आमतौर पर असंभव है, और System.exit(...)(या समतुल्य) आपका एकमात्र विकल्प है,
स्टीफन सी

1
@StephenC क्या हम सोच सकते हैं कि यदि "अभिभावक" वर्ग लोड करने में विफल रहता है, तो अपने कोड पर काम नहीं करने के बाद से इसके आश्रित वर्ग को लोड करना अनावश्यक है? क्या आप किसी ऐसे मामले का कुछ उदाहरण प्रदान कर सकते हैं जहाँ पर इस तरह के आश्रित वर्ग को लोड करना आवश्यक होगा? धन्यवाद
Benj

कैसे के बारे में ... यदि कोड इसे गतिशील रूप से लोड करने का प्रयास करता है; जैसे Class.forName के माध्यम से।
स्टीफन सी

21

इसे इस तरह देखना होगा (यह वैध जावा कोड नहीं है)

// Not a valid Java Code
static throws SomeCheckedException {
  throw new SomeCheckedException();
}

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

static {
  try {
     ClassA a = new ClassA();
     Class<ClassB> clazz = Class.forName(ClassB.class);
     String something = ClassC.SOME_STATIC_FIELD;
  } catch (Exception oops) {
     // anybody knows which type might occur?
  }
}

और एक और गंदी बात -

interface MyInterface {
  final static ClassA a = new ClassA();
}

कल्पना कीजिए कि क्लासए के पास एक स्टैटिक इनिशियलाइज़र था, जो एक अपवाद को फेंक रहा था: इस मामले में MyInterface (जो कि 'हिडन' स्टैटिक इनिशियलाइज़र वाला इंटरफ़ेस है) को अपवाद को फेंकना होगा या उसे संभालना होगा - एक इंटरफ़ेस पर अपवाद को हैंडल करना? बेहतर है कि इसे वैसे ही छोड़ दें।


7
mainजाँच अपवाद छोड़ सकते हैं। जाहिर है कि उन्हें संभाला नहीं जा सकता।
मैकेनिकल घोंघा

@ मेकेनिसेल: दिलचस्प बिंदु। जावा के मेरे मानसिक मॉडल में, मुझे लगता है कि एक "जादुई" (डिफ़ॉल्ट) थ्रेड है। UncaughtExceptionHandler उस थ्रेड से जुड़ा हुआ है जो main()स्टैक ट्रेस के साथ अपवाद को प्रिंट करता है System.err, फिर कॉल करता है System.exit()। अंत में, इस सवाल का जवाब शायद है: "क्योंकि जावा डिजाइनरों ने ऐसा कहा था"।
केविनर्पपे

7

जावा स्थैतिक आरंभीकरण खंड से जाँच अपवाद को फेंकने की अनुमति क्यों नहीं देता है?

तकनीकी रूप से, आप ऐसा कर सकते हैं। हालांकि, चेक किए गए अपवाद को ब्लॉक के भीतर पकड़ा जाना चाहिए। ब्लॉक के बाहर प्रचारित अपवाद की अनुमति नहीं है ।

तकनीकी रूप से, एक स्थिर इनिशियलाइज़र ब्लॉक 1 से बाहर प्रचारित करने के लिए अनियंत्रित अपवाद की अनुमति देना भी संभव है । लेकिन जानबूझकर ऐसा करना बहुत बुरा विचार है! समस्या यह है कि जेवीएम स्वयं अनियंत्रित अपवाद को पकड़ता है, और इसे लपेटता है और इसे ए के रूप में पुनर्व्यवस्थित करता है ExceptionInInitializerError

NB: Errorयह एक नियमित अपवाद नहीं है। आपको इससे उबरने का प्रयास नहीं करना चाहिए।

ज्यादातर मामलों में, अपवाद नहीं पकड़ा जा सकता है:

public class Test {
    static {
        int i = 1;
        if (i == 1) {
            throw new RuntimeException("Bang!");
        }
    }

    public static void main(String[] args) {
        try {
            // stuff
        } catch (Throwable ex) {
            // This won't be executed.
            System.out.println("Caught " + ex);
        }
    }
}

$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
    at Test.<clinit>(Test.java:5)

कहीं नहीं है आप 2try ... catch को पकड़ने के लिए ऊपर में रख सकते हैं ।ExceptionInInitializerError

कुछ मामलों में आप इसे पकड़ सकते हैं। उदाहरण के लिए, यदि आपने कॉल करके क्लास इनिशियलाइज़ेशन को ट्रिगर किया है, तो आप Class.forName(...)कॉल को एक या एक या बाद में tryपकड़ सकते हैं ।ExceptionInInitializerErrorNoClassDefFoundError

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

1 - यह एक संकलित त्रुटि है अगर एक स्थिर ब्लॉक बिना शर्त अनियंत्रित अपवाद को फेंकता है।

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

3 - यदि आप असफल कक्षाओं को पुनर्प्राप्त करना चाहते हैं, तो आपको उन लोडर से छुटकारा पाने की आवश्यकता होगी जो उन्हें लोड करते हैं।


इस डिजाइन निर्णय के पीछे क्या कारण था?

यह प्रोग्रामर को उस कोड को लिखने से बचाने के लिए है जो अपवादों को फेंकता है जिन्हें संभाला नहीं जा सकता है!

जैसा कि हमने देखा है, एक स्टेटिक इनिशियलाइज़र में एक अपवाद एक विशिष्ट एप्लिकेशन को एक ईंट में बदल देता है। सबसे अच्छी बात यह है कि भाषा डिजाइनर एक संकलित त्रुटि के रूप में चेक किए गए मामले से निपटने के लिए कर सकते हैं। (दुर्भाग्य से, अनियंत्रित अपवादों के लिए भी ऐसा करना व्यावहारिक नहीं है।)


ठीक है, तो आपको क्या करना चाहिए अगर आपके कोड को "" की जरूरत है "एक स्थिर इनिशियलाइज़र में अपवादों को फेंकने के लिए। असल में, दो विकल्प हैं:

  1. यदि (पूर्ण!) ब्लॉक के भीतर अपवाद से वसूली संभव है, तो ऐसा करें।

  2. अन्यथा, अपने कोड का पुनर्गठन करें ताकि आरंभिक स्थैतिक आरंभीकरण खंड (या स्थैतिक चर के आरंभक) में न हो।


क्या कोड की संरचना के बारे में कोई सामान्य सिफारिशें हैं ताकि यह कोई स्थैतिक आरंभ न करे?
मास्टर जॉय 2

ये समाधान कैसे ध्वनि करते हैं? stackoverflow.com/a/21321935/6648326 और stackoverflow.com/a/56575807/6648326
MasterJoe2

1
1) मेरे पास कोई नहीं है। 2) उन्हें बुरा लगता है। उन टिप्पणियों को देखें जिन्हें मैंने उन पर छोड़ा था। लेकिन मैं केवल वही उत्तर दोहरा रहा हूं जो मैंने अपने उत्तर में कहा था। यदि आप मेरे उत्तर को पढ़ते और समझते हैं, तो आपको पता होगा कि "समाधान" समाधान नहीं हैं।
स्टीफन सी।

4

जावा लैंग्वेज स्पेसिफिकेशन्स पर एक नज़र डालें : यह कहा गया है कि यह एक संकलित समय त्रुटि है यदि स्थिर इनिशियलाइज़र विफल रहता है, तो एक चेक किए गए अपवाद के साथ अचानक पूरा करने में सक्षम है।


5
हालांकि इस सवाल का जवाब नहीं है। उन्होंने पूछा कि यह एक संकलन समय त्रुटि क्यों है।
विंस्टन स्मिथ

हम्म, इसलिए किसी भी RuntimeError को फेंकना संभव होना चाहिए, क्योंकि JLS केवल अपवादों की जाँच करता है।
एंड्रियास डॉक

यह सही है, लेकिन आप कभी भी एक स्टैकट्रेस के रूप में नहीं देखेंगे। इसलिए आपको स्थैतिक आरंभीकरण ब्लॉकों से सावधान रहने की आवश्यकता है।
EJB

2
@ ईजेबी: यह गलत है। मैंने अभी इसे आज़माया है और निम्नलिखित कोड ने मुझे एक दृश्य स्टैकट्रेस दिया है: public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }आउटपुट:Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: whathappenswhenastaticblockthrowsanexception at Main.<clinit>(Main.java:6) Caused by: java.lang.ClassNotFoundException: whathappen...
कोनराड हॉफनर

"के कारण" भाग में स्टैक ट्रेस से पता चलता है कि आप शायद अधिक रुचि रखते हैं।
लेडीकैलेन

2

चूंकि आपके द्वारा लिखा गया कोई भी कोड स्टेटिक इनिशियलाइज़ेशन ब्लॉक नहीं कह सकता है, इसलिए चेक को फेंकना उपयोगी नहीं है exceptions। यदि यह संभव था, जब जाँच किए गए अपवादों को फेंक दिया जाता है तो jvm क्या करेगा? Runtimeexceptionsप्रचारित हैं।


1
अच्छा, हाँ अब मुझे बात समझ में आई। इस तरह का प्रश्न पोस्ट करना मेरे लिए बहुत मूर्खतापूर्ण था। लेकिन अफसोस ... मैं इसे अब नहीं हटा सकता। :( फिर भी, आपकी प्रतिक्रिया के लिए +1 ...
लापता

1
@, दरअसल, चेक किए गए अपवादों को RuntimeException में परिवर्तित नहीं किया गया है। यदि आप खुद बाइटकोड लिखते हैं, तो आप अपने दिल की सामग्री के लिए एक स्थिर इनिशियलाइज़र के अंदर जाँच किए गए अपवादों को फेंक सकते हैं। JVM अपवाद जाँच की बिल्कुल परवाह नहीं करता है; यह विशुद्ध रूप से जावा भाषा का निर्माण है।
एंटीमनी ऑक्ट

0

उदाहरण के लिए: स्प्रिंग का डिस्पैचरसर्वलेट (org.springframework.web.servlet.DispatcherServlet) उस परिदृश्य को संभालता है जो एक चेक अपवाद को पकड़ता है और दूसरा अनियंत्रित अपवाद को फेंकता है।

static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }

1
यह समस्या उस समस्या के पास है जिसे अनियंत्रित अपवाद नहीं पकड़ा जा सकता है। इसके बजाय यह वर्ग और किसी भी अन्य वर्गों को रखता है जो उस पर निर्भर नहीं करते हैं।
स्टीफन सी।

@StephenC - क्या आप कृपया एक सरल उदाहरण दे सकते हैं जिसमें हम एक पुनर्प्राप्ति योग्य राज्य चाहते हैं?
मास्टरजो 2

Hypothetically ... यदि आप IOException से पुनर्प्राप्त करने में सक्षम होना चाहते थे ताकि आवेदन जारी रह सके। यदि आप ऐसा करना चाहते हैं, तो आपको अपवाद को पकड़ना चाहिए और वास्तव में इसे संभालना चाहिए ... अनियंत्रित अपवाद को नहीं फेंकना चाहिए।
स्टीफन सी।

-5

मैं एक जाँच अपवाद भी फेंकने में सक्षम हूँ ...।

static {
    try {
        throw new IOException();
    } catch (Exception e) {
         // Do Something
    }
}

3
हाँ, लेकिन आप इसे स्टैटिक ब्लॉक के भीतर पकड़ रहे हैं। आपको स्थैतिक ब्लॉक के अंदर से इसे बाहर तक एक चेक किए गए अपवाद को फेंकने की अनुमति नहीं है।
आर्टऑफवर्फ 18
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.