2012-03-13 13 views
8

जावा में कक्षा पर एक स्थिर विधि को कॉल करने से स्थिर इनटाइलाइजेशन ब्लॉक को निष्पादित करने के लिए ट्रिगर किया जाता है?जावा में स्टेटिक प्रारंभकर्ता और स्टेटिक तरीके

अनुभवजन्य, मैं नहीं कहूंगा। मैं कुछ इस तरह है:

public class Country { 
    static { 
     init(); 
     List<Country> countries = DataSource.read(...); // get from a DAO 
     addCountries(countries); 
    } 

    private static Map<String, Country> allCountries = null; 

    private static void init() { 
     allCountries = new HashMap<String, Country>(); 
    } 

    private static void addCountries(List<Country> countries) { 
     for (Country country : countries) { 
      if ((country.getISO() != null) && (country.getISO().length() > 0)) { 
       allCountries.put(country.getISO(), country); 
      } 
     } 
    } 

    public static Country findByISO(String cc) { 
     return allCountries.get(cc); 
    } 
} 

कोड में वर्ग का उपयोग कर, मैं की तरह कुछ कार्य करें:

Country country = Country.findByISO("RO"); 

समस्या यह है कि मैं एक NullPointerException मिलता है क्योंकि नक्शा (allCountries) प्रारंभ नहीं किया जाता है। यदि मैंने static ब्लॉक में ब्रेकपॉइंट्स सेट अप किए हैं, तो मैं मानचित्र को सही तरीके से पॉप्युलेट कर रहा हूं, लेकिन ऐसा लगता है कि स्थैतिक विधि को प्रारंभकर्ता को निष्पादित करने का कोई ज्ञान नहीं है।

क्या कोई इस व्यवहार को समझा सकता है?


अद्यतन: मैं कोड के लिए और अधिक विस्तार जोड़ दिया है। यह अभी भी 1: 1 नहीं है (वहां कई मानचित्र हैं और अधिक तर्क हैं), लेकिन मैंने स्पष्ट रूप से allCountries की घोषणाओं/संदर्भों को देखा है और वे ऊपर सूचीबद्ध हैं।

आप पूर्ण प्रारंभिक कोड here देख सकते हैं।

अद्यतन # 2: मैंने जितना संभव हो सके कोड को सरल बनाने की कोशिश की और इसे फ्लाई पर लिखा। प्रारंभिक कोड के बाद वास्तविक कोड स्थिर स्थिर घोषणा थी। इसने संदर्भ को रीसेट करने का कारण बना दिया, क्योंकि जॉन ने नीचे दिए गए जवाब में बताया।

मैंने इसे प्रतिबिंबित करने के लिए मेरे पोस्ट में कोड को संशोधित किया है, इसलिए यह प्रश्न ढूंढने वाले लोगों के लिए स्पष्ट है। हर किसी के भ्रम के बारे में खेद है। मैं सिर्फ हर किसी के जीवन को आसान बनाने की कोशिश कर रहा था :)।

आपके उत्तरों के लिए धन्यवाद!

+2

क्या आप मानचित्र को प्रारंभ करने के साथ कोड दिखा सकते हैं? – Tom

+1

बीटीडब्ल्यू आप अपने उदाहरण में findByISO() विधि का रिटर्न प्रकार खो रहे हैं। –

उत्तर

26

जावा में कक्षा पर एक स्थिर विधि को कॉल करने से स्थिर इनटाइलाइजेशन ब्लॉक को निष्पादित करने के लिए ट्रिगर किया जाता है?

अनुभवजन्य, मैं नहीं कहूंगा।

आप गलत हैं।

JLS section 8.7 से: जब वर्ग आरंभ नहीं हो जाता

एक स्थिर प्रारंभकर्ता एक वर्ग में घोषित क्रियान्वित किया जाता है (§12.4.2)। कक्षा चर (§8.3.2) के लिए किसी भी फ़ील्ड प्रारंभकर्ता के साथ, कक्षा के वर्ग चर को प्रारंभ करने के लिए स्थिर प्रारंभकर्ताओं का उपयोग किया जा सकता है।

JLS की Section 12.4.1 राज्यों:

  • टी एक वर्ग है:

    एक वर्ग या इंटरफ़ेस प्रकार टी निम्नलिखित में से किसी एक की पहली आवृत्ति से ठीक पहले प्रारंभ हो जाएगा और टी का एक उदाहरण बनाया गया है।

  • टी एक वर्ग है और टी द्वारा घोषित एक स्थिर विधि का आह्वान किया जाता है।

  • टी द्वारा घोषित एक स्थिर क्षेत्र असाइन किया गया है।

  • टी द्वारा घोषित एक स्थिर क्षेत्र का उपयोग किया जाता है और फ़ील्ड निरंतर परिवर्तनीय नहीं है (§4.12.4)।

  • टी एक शीर्ष स्तर की कक्षा (§7.6) है, और एक जोरदार कथन (§14.10) टी (§8.1.3) के भीतर निहित रूप से घिरा हुआ है।

यह आसानी से दिखाया गया है:

class Foo { 
    static int x = 0; 
    static { 
     x = 10; 
    } 

    static int getX() { 
     return x; 
    } 
} 

public class Test { 
    public static void main(String[] args) throws Exception { 
     System.out.println(Foo.getX()); // Prints 10 
    } 
} 

आपकी समस्या कोड है कि आप हमें नहीं दिखा था के कुछ हिस्से में है।

static { 
    Map<String, Country> allCountries = new HashMap<String, Country>(); 
    // Add entries to the map 
} 

कि खाल स्थिर चर, स्थिर चर अशक्त छोड़ने: मेरी अनुमान कि आप वास्तव में एक स्थानीय चर घोषणा कर रहे हैं, इस तरह है।

static { 
    allCountries = new HashMap<String, Country>(); 
    // Add entries to the map 
} 

संपादित करें: यदि यह मामला है, बस एक काम के बजाय एक घोषणा के लिए इसे बदल एक बात ध्यान देने योग्य है - हालांकि आप अपने स्थिर प्रारंभकर्ता की बहुत पहली पंक्ति के रूप init() मिल गया है, अगर आप वास्तव में से पहले कुछ भी कर रहे हैं (संभवतः अन्य परिवर्तनीय प्रारंभकर्ताओं में) जो किसी अन्य वर्ग को कॉल करते हैं, और उस वर्ग को वापस पर आपके Country कक्षा में कॉल किया जाता है, तो उस कोड को निष्पादित किया जाएगा जबकि allCountries अभी भी शून्य है।

संपादित करें: ठीक है, अब हम आपका असली कोड देख सकते हैं, मुझे समस्या मिली है। आपका पद कोड इस है:

private static Map<String, Country> allCountries; 
static { 
    ... 
} 

लेकिन अपने असली कोड इस है:

static { 
    ... 
} 
private static Collection<Country> allCountries = null; 

दो महत्वपूर्ण यहाँ मतभेद हैं:

  • चर घोषणा होती है के बाद स्थिर प्रारंभिक - प्रारंभ शाब्दिक आदेश में होता है चर initializers स्थिर प्रारंभकर्ता से पहले सभी रन नहीं हैं: alizer ब्लॉक
  • चर घोषणा एक स्पष्ट काम शून्य पर

उन के संयोजन आप खिलवाड़ कर रहा है भी शामिल है।

तो आप संग्रह को पॉप्युलेट कर रहे हैं ... और फिर संदर्भ को शून्य पर सेट कर रहे हैं।

JLS की Section 12.4.2 आरंभीकरण के चरण 9 में यह गारंटी देता है:

इसके बाद, वे या तो वर्ग चर initializers और वर्ग के स्थिर initializers, या इंटरफेस के क्षेत्र initializers निष्पादित शाब्दिक क्रम में, जैसे कि वे एक ही ब्लॉक थे।

प्रदर्शन कोड:

class Foo { 

    private static String before = "before"; 

    static { 
     before = "in init"; 
     after = "in init"; 
     leftDefault = "in init"; 
    } 

    private static String after = "after"; 
    private static String leftDefault; 

    static void dump() { 
     System.out.println("before = " + before); 
     System.out.println("after = " + after); 
     System.out.println("leftDefault = " + leftDefault); 
    } 
} 

public class Test { 
    public static void main(String[] args) throws Exception { 
     Foo.dump(); 
    } 
} 

आउटपुट:

before = in init 
after = after 
leftDefault = in init 

तो समाधान है या तो शून्य पर, या घोषणाओं स्थानांतरित करने के लिए स्पष्ट काम से छुटकारा पाने के (और इसलिए प्रारंभकर्ता) स्थिर प्रारंभकर्ता से पहले, या (मेरी वरीयता) दोनों।

+0

धन्यवाद। मैंने मानचित्र के संदर्भों की जांच की है और वे ठीक हैं, मैं स्थैतिक चर का संदर्भ दे रहा हूं, स्थानीय घोषित नहीं कर रहा हूं। मैंने अंतर्दृष्टि प्रदान करने के लिए और कोड पोस्ट किया है। –

+0

@AlexCiminian: ठीक है कि यह निश्चित रूप से आपका असली कोड नहीं है - 'findByISO' विधि में रिटर्न प्रकार नहीं है। मुझे अभी भी यकीन है कि समस्या आपके कोड में है ... हालांकि मेरे पास एक और विचार है। संपादित करेंगे –

+0

'init() 'वास्तव में प्रारंभकर्ता की पहली पंक्ति है और यह किसी भी अन्य वर्ग को कॉल नहीं करता है जिसमें देश के संदर्भ हैं। यह सिर्फ 'देश' वर्ग के अंदर कई धारकों को शुरू करता है। आप मेरे संपादन में पोस्ट किए गए हस्टबिन लिंक में पूर्ण कोड देख सकते हैं (पोस्ट के अंत में)। –

2

क्लास लोड होने पर स्थैतिक प्रारंभकर्ता को बुलाया जाएगा, जो आमतौर पर पहली बार उल्लिखित होता है। इसलिए एक स्थैतिक विधि को कॉल करना वास्तव में प्रारंभकर्ता को ट्रिगर करेगा यदि कक्षा पहली बार संदर्भित हो।

क्या आप सुनिश्चित हैं कि शून्य सूचक अपवाद allcountries.get() से है, और शून्य Country से get() पर वापस नहीं आया है? दूसरे शब्दों में, क्या आप निश्चित हैं जो ऑब्जेक्ट शून्य है?

+0

हाँ, मुझे यकीन है कि शून्य मानचित्र की वजह से अपवाद ट्रिगर हो गया है। स्पष्टीकरण के लिए –

2

सैद्धांतिक रूप से, स्थिर श्रेणी ब्लॉक क्लास लोडर द्वारा निष्पादित किया जाना चाहिए कक्षा को लोड करता है।

Country country = Country.findByISO("RO"); 
^ 

अपने कोड में, यह पहली बार आप का उल्लेख वर्ग देश (शायद ऊपर लाइन) आरंभ नहीं हो जाता।

public class Country { 
    private static Map<String, Country> allCountries; 
    static { 
     allCountries = new HashMap<String, Country>(); 
     allCountries.put("RO", new Country()); 
    } 

    public static Country findByISO(String cc) { 
     return allCountries.get(cc); 
    } 
} 
इस के साथ

:

मैं इस भाग गया

public class Start 
{ 
    public static void main(String[] args){ 
     Country country = Country.findByISO("RO"); 
     System.out.println(country); 
    } 
} 

और सब कुछ सही ढंग से काम किया। क्या आप त्रुटि के स्टैक ट्रेस पोस्ट कर सकते हैं?

मैं कहूंगा कि समस्या इस तथ्य में निहित है कि स्थैतिक ब्लॉक वास्तविक क्षेत्र से पहले घोषित किया गया है।

0

क्या आपके पास अपने स्थिर प्रारंभकर्ता ब्लॉक में allCountries = new HashMap(); है? स्थिर प्रारंभकर्ता ब्लॉक वास्तव में class initialization पर है।