2015-05-12 3 views
33

मैं हाल ही में इस काम पर आया था। हालांकि मुझे यकीन नहीं है कि यह वास्तव में एक अच्छा विचार है, मुझे समझ में नहीं आता कि कंपाइलर द्वारा स्थिर ब्लॉक कैसे प्रबंधित किए जाते हैं।स्थिर ब्लॉक के बीच ऑब्जेक्ट निर्भरता कैसे हल की जाती है?

पर विचार करें आप कक्षाएं A और B है:

public class A { 

    public final static List<Integer> list; 
    static { 
     list = new ArrayList<>(); 
    } 
} 

public class B { 

    public final static int dependsOnA; 
    static { 
     dependsOnA = A.list.size(); 
    } 
} 

और एक मुख्य वर्ग है कि बस B.dependsOnA पढ़ता

यहाँ एक उदाहरण है।

B में स्थिर ब्लॉक A में से एक पर निर्भर है, क्योंकि यह list स्थिर चर का उपयोग करता है।

अब, कोड ठीक से निष्पादित करता है और रनटाइम पर NullPointerException उठाया जाता है। लेकिन यह तंत्र क्या है जो सुनिश्चित करता है कि list को संभावित रूप से कहीं और इस्तेमाल होने से पहले शुरू किया गया है?

+3

* डिज़ाइन * पॉइंट-ऑफ-व्यू से, मैं सौहार्दपूर्ण सुझाव देता हूं कि कक्षा 'ए' के लिए सार्वजनिक * इसकी' सूची 'संपत्ति बनाने के लिए यह एक अच्छा विचार नहीं है। इसके बजाय, मैं इसे निजी बना देता, और 'ए' में * विधियों * प्रदान करता था जो अन्य सभी वर्गों को "उन चीजों को जो उन्हें जानने की ज़रूरत होती है, और उन चीजों को करने की क्षमता देता है जिन्हें उन्हें करने की आवश्यकता होती है।" एक बहुत ही महत्वपूर्ण लाभ यह है कि, अब, कक्षा 'ए' दोनों आत्मनिर्भर और आत्म-वर्णन दोनों हैं। यह सुनिश्चित करने के लिए आवश्यक सभी कोड कि वर्ग 'ए' सही तरीके से काम कर रहा है, * * वर्ग 'ए' में है, जिसका कोड हस्तक्षेप या साइड इफेक्ट्स (जैसे 'बी') से संरक्षित है। –

+1

... भी: "ए 'क्या कर सकता है, और अन्य लोग' ए' 'के साथ क्या कर सकते हैं? * प्रश्न * केवल * ए * पर देखकर, इस प्रश्न का उत्तर अब निश्चित रूप से दिया जा सकता है। दोनों के बीच रनटाइम अंतर नगण्य है, लेकिन डिजाइन, आईएमएचओ, अधिक मजबूत और डिबगबल है। (और, बहुत कम निर्भर करता है "बिल्कुल जब जावा यह करता है या वह।") मैं प्रस्तुत करता हूं कि यह कहना एक अच्छा सिद्धांत है: "अन्य वर्गों को अन्य वर्गों के मामलों में दखल न दें।" जितना अधिक यह इंसानों के लिए बुद्धिमान है ("जो कुरकुरे हैं, और फवा बीन्स के साथ अच्छा स्वाद ...") ड्रैगन के साथ दखल देने के लिए नहीं। –

+0

यदि सूची पहले से ही स्थिर है तो Alist.size() प्रत्यक्ष पहुंच अधिक कुशल हो सकती है? –

उत्तर

8

B की static ब्लॉक के निष्पादन के दौरान, क्रम पहली बार के लिए A का सामना करना पड़ता है, और यह A की static ब्लॉक आह्वान से पहले ही A.list तक पहुँचता है।

+1

'स्थिर' ब्लॉक शब्द 'स्थिर' कन्स्ट्रक्टर से अधिक आम है। सुनिश्चित नहीं है कि कोई वास्तव में इस शब्द का उपयोग करता है। – CKing

+1

धन्यवाद चेतन, मैंने इसे अभी बदल दिया। मैं कई अन्य भाषाओं का उपयोग करता हूं (उदाहरण के लिए सी #) जहां इसे स्थिर कन्स्ट्रक्टर कहा जाता है। – Glorfindel

7

कोई फर्क नहीं पड़ता कि आप कोड कैसे लिखते हैं, static ब्लॉक static ब्लॉक है और यह एक कक्षा लोड करने वाले JVM के हिस्से के रूप में निष्पादित होगा।

जब आप कहते हैं कि B.dependsOnA, B वर्ग JVM द्वारा लोड geting शुरू होता है और B में static ब्लॉक इस प्रक्रिया के दौरान कहीं भी कहा जाता हो जाता है। जब आप dependsOnA = A.list.size(); कहते हैं, तो कक्षा A जेवीएम द्वारा लोड होने लगती है और static ब्लॉक ए में इस प्रक्रिया के दौरान निष्पादित होता है जो list प्रारंभ करता है। स्टेटमेंट list.size() क्लास A पूरी तरह से JVM द्वारा लोड किया गया है के बाद निष्पादित होगा। इसके बाद, बी पूर्णता में स्थिर ब्लॉक के बाद JVM केवल B लोडिंग को समाप्त कर सकता है।

9

"तंत्र" जेवीएम का क्लासलोडर है, जो यह सुनिश्चित करेगा कि वर्ग के पहले संदर्भ के लिए नियंत्रण प्रवाह लौटने से पहले कक्षा में इनटाइलाइजेशन ब्लॉक (पूरे जेवीएम में एक वैश्विक लॉक के साथ) निष्पादित किया जाएगा। यह पहले संदर्भ के बाद केवल A कक्षा लोड करेगा, इस मामले में जब B के इनिट ब्लॉक संदर्भ A.list

5

यहाँ हम कुछ explaination Static Block in Java

है आप एक वर्ग पहले कॉल करते हैं, एक स्थिर कहा जाता है और A.list मौजूद है और जब बी इसे कहते जाएगा।

यदि आप बी कक्षा को पहले कॉल करते हैं, तो बी स्टेटिक को कॉल के लिए कैस्केडिंग कहा जाता है, जो इसके स्थिर ब्लॉक को कॉल करता है, जहां एलिस्ट बनाई जाती है।

हम देख सकते हैं अपने trickiest तरीका है: B> B.static> एक> A.static> A.list

4

मौजूद है काम कर बहुत ही सरल JVM वर्ग लोडर, जो यह सुनिश्चित करेंगे कि एक वर्ग स्थिर है जब कक्षा का संदर्भ दिया जाता है तो ब्लॉक को निष्पादित किया जाता है।
1।यदि आपके पास स्थैतिक ब्लॉक में निष्पादन योग्य बयान हैं, तो JVM स्वचालित रूप से इन कथन निष्पादित करेगा जब वर्ग JVM में लोड हो जाएगा।
2. यदि आप स्थैतिक ब्लॉक से कुछ स्थैतिक चर/विधियों का जिक्र कर रहे हैं, तो कक्षाओं को ऊपर के रूप में जेवीएम में लोड करने के बाद ये कथन निष्पादित किए जाएंगे, यानी स्थिर स्थिर चर/विधियों को संदर्भित किया जाएगा और स्थिर ब्लॉक दोनों होंगे मार डाला।

34

तंत्र विस्तार here में वर्णन किया गया है, लेकिन पांच सबसे महत्वपूर्ण बिंदु हैं:

  1. से पहले एक वर्ग संदर्भित है, यह initialised किया जाना चाहिए।
  2. यदि कक्षा का प्रारंभिकरण पहले ही शुरू हो चुका है (या यदि यह समाप्त हो गया है), तो इसका पुन: प्रयास नहीं किया जाता है।
  3. कक्षा शुरू होने से पहले, इसके सभी सुपरक्लास और सुपरइंटरफेस को पहले आरंभ करने की आवश्यकता है।
  4. एक वर्ग के भीतर स्थिर प्रारंभिक पाठकों को क्रमिक क्रम में निष्पादित किया जाता है।
  5. कार्यान्वित इंटरफेस को उस क्रम में शुरू किया गया है जिसमें वे implements खंड में दिखाई देते हैं।

ये नियम पूरी तरह से उस आदेश को परिभाषित करते हैं जिसमें स्थिर ब्लॉक निष्पादित किए जाते हैं।

आपका मामला नहीं बल्कि सरल है: इससे पहले कि आप का उपयोग B.dependsOnA, B (नियम 1) initialised करने की आवश्यकता है, स्थिर initialiser तो A.list, जो वर्ग A की initialisation चलाता है का उपयोग करने की कोशिश कर रहा है (फिर से 1 शासन)।

नोट कुछ भी नहीं परिपत्र निर्भरता इस तरह से है, जो दिलचस्प बातें होती हैं करने के लिए कारण होगा बनाने से क्या रोक रहा है कि:

public class Bar { 
    public static int X = Foo.X+1; 

    public static void main(String[] args) { 
     System.out.println(Bar.X+" "+Foo.X); // 
    } 

} 

class Foo { 
    public static int X = Bar.X+1; 
} 

परिणाम यहाँ 2 1 है, क्योंकि जिस तरह से initialisation होता यह है:

  1. Bar प्रारंभिकरण शुरू होता है।
  2. Bar.X प्रारंभिक मूल्य का मूल्यांकन किया जाता है, जो Foo पहले
  3. Foo रों initialisation शुरू होता है initialising की आवश्यकता है।
  4. Foo.X प्रारंभिक मूल्य मूल्यांकन किया जाता है, लेकिन जब से Bar रों initialisation प्रगति में पहले से ही है, यह फिर से initialised नहीं है, Bar.X रों "वर्तमान" मूल्य जो 0 है प्रयोग किया जाता है, इस प्रकार Foo.X 1.
  5. को initialised है हम वापस Bar.X मूल्य का मूल्यांकन करने के लिए कर रहे हैं, Foo.X 1 इसलिए Bar.X हो जाता है 2.

यह भले ही दोनों क्षेत्रों final घोषित किया गया काम करता है।

कहानी का नैतिक स्थैतिक प्रारंभिक व्यक्तियों के साथ सावधान रहना है जो एक ही पुस्तकालय या आवेदन में अन्य वर्गों का जिक्र करते हैं (किसी तृतीय पक्ष पुस्तकालय या कक्षा वर्ग पुस्तकालय में कक्षाओं का जिक्र करना सुरक्षित है क्योंकि वे वापस संदर्भ नहीं दे रहे हैं आपकी कक्षा)।

6

यह कक्षा लोडर का काम है।जावा में वर्ग लोडिंग बूटस्ट्रैप क्लासलोडर से शुरू होती है। यह वर्ग लोडर पहले मानक जावा पुस्तकालय, rt.jar में सभी वर्गों को लोड करता है।

फिर एक्सटेंशन क्लासलोडर का आह्वान किया गया है। यह एक JVM ext निर्देशिका में स्थापित एक्सटेंशन जार फ़ाइलों से सभी वर्गों को लोड करता है। अब अंततः कक्षापाथ क्लासलोडर का आह्वान किया जाता है।

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

तो, ऐसा कोई मौका नहीं है कि गैर-लोड कक्षाओं के लिए कोई भी कॉल हो। चलो अपने स्वयं के उदाहरण है, जिसका कोड इस तरह है के साथ इस देखें:

class A 
{ 
    public final static List<Integer> list; 
    static 
    { 
     System.out.println("Loaded Class A"); 
     list = new ArrayList<>(); 
    } 
} 

class B 
{ 
    public final static int dependsOnA; 
    static 
    { 
     System.out.println("Loaded Class B"); 
     dependsOnA = A.list.size(); 
    } 
} 

यहां इस उदाहरण में, वहाँ वास्तव में कोई मुख्य विधि है, इसलिए इन कक्षाओं में वास्तव में मेमोरी में लोड नहीं किया जाएगा। मान लीजिए, चलिए उपरोक्त कोड में निम्नलिखित मुख्य श्रेणी जोड़ें।

class C 
{ 
    static 
    { 
     System.out.println("Loaded Class C"); 
    } 

    public static void main(String[] args) 
    { 
     System.out.println(B.dependsOnA); 
    } 
} 

देखते हैं क्या इस उत्पादन में उत्पादन होता है: http://ideone.com/pLg3Uh

Loaded Class C 
Loaded Class B 
Loaded Class A 
0 

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

अब मुख्य विधि, हम वर्ग बी के dependsOnA का मूल्य अब मुद्रित, वर्ग लोडर कि बयान को क्रियान्वित बंद हो जाता है, और वर्ग बी लोड करता है, और यह स्थिर ब्लॉक, जो बारी में, के साथ प्रदान करती है dependsOnA चर है कार्यान्वित कक्षा ए की सूची में तत्वों की संख्या का मूल्य, जो लोड नहीं होता है।

तो कक्षा लोडर वहां से कूदता है, अब कक्षा को लोड करता है, और कक्षा ए के स्थिर ब्लॉक को आमंत्रित करता है, और एक सूची बनाई जाती है। अब चूंकि लोड करने के लिए और कक्षाएं नहीं हैं, इसलिए कक्षा लोडर कक्षा बी के स्थिर ब्लॉक पर वापस आता है, और असाइनमेंट पूरा हो गया है। अब अंत में, नियंत्रण मुख्य विधि के साथ है, और dependsOnA का मान कंसोल पर मुद्रित है।

उम्मीद है कि इससे मदद मिलती है।

संबंधित मुद्दे