यह NullPointerException को क्यों नहीं फेंक रहा है?


89

निम्नलिखित कोड के लिए मनका स्पष्टीकरण:

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample.append("B");
System.out.println(sample);

यह प्रिंट करेगा Bताकि साबित होता है sampleऔर referToSampleऑब्जेक्ट्स एक ही मेमोरी संदर्भ को संदर्भित करते हैं।

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
sample.append("A");
referToSample.append("B");
System.out.println(referToSample);

यह प्रिंट करेगा ABजो भी साबित होता है।

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
referToSample.append("A");
System.out.println(sample);

जाहिर है यह फेंक देगा NullPointerExceptionक्योंकि मैं appendएक अशक्त संदर्भ पर कॉल करने की कोशिश कर रहा हूं ।

StringBuilder sample = new StringBuilder();
StringBuilder referToSample = sample;
referToSample = null;
sample.append("A");
System.out.println(sample);

इसलिए यहाँ मेरा प्रश्न है, अंतिम कोड का नमूना क्यों नहीं फेंका जा NullPointerExceptionरहा है क्योंकि जो मैं पहले दो उदाहरणों से देख रहा हूँ और समझ रहा हूँ वह यह है कि यदि दो वस्तुएं एक ही वस्तु का जिक्र करती हैं तो यदि हम किसी भी मूल्य को बदलते हैं तो यह अन्य को भी प्रतिबिंबित करेगा क्योंकि दोनों ही इंगित कर रहे हैं एक ही स्मृति संदर्भ। तो वह नियम यहां क्यों नहीं लागू हो रहा है? अगर मैं nullreferToSample को असाइन करता हूं तो नमूना भी अशक्त होना चाहिए और इसे NullPointerException को फेंक देना चाहिए लेकिन यह एक नहीं फेंक रहा है, क्यों?


31
sampleअभी भी है sample। आप ही बदल गए referToSample
डेव न्यूटन

25
Upvoted / अभिनय किया! बहुत बुनियादी सवाल है, लेकिन यह आपकी समस्या को समझाने और सवाल को अच्छी तरह से पूछने का एक सुंदर उदाहरण है ।
रयान रैंसफोर्ड

15
आपके प्रश्न में शब्दावली का एक बिंदु: आप वस्तुओं के रूप में sampleऔर वस्तुओं का उल्लेख करते रहते हैं, लेकिन वे वस्तुएं नहीं हैं, वे चर हैं। एक चर एक वस्तु के संदर्भ में पकड़ सकता है, लेकिन यह स्वयं एक वस्तु नहीं है। यह एक सूक्ष्म अंतर है, लेकिन यह मूल रूप से आपके भ्रम का सार है। referToSample
डेनियल प्राइडेन

1
यह ऑब्जेक्ट चर के बारे में सिर्फ संकेत के रूप में सोचने में मदद करता है। किसी भी ऑपरेटर जो एक चर पर काम करता है ( volatile, final, =, ==...) एक वस्तु वैरिएबल पर लागू किया जब प्रभावित सूचक , नहीं वस्तु यह करने के लिए संदर्भित करता है।
माइकफेह सेप

2
@ अर्पित मैं सम्मानपूर्वक असहमत हूं। इस प्रश्न में एक बड़ी अवधारणा छिपी हुई है, और वह है, किसी वस्तु और उस वस्तु के संदर्भ के बीच का अंतर । अधिकांश समय, हमें इस अंतर से अवगत होने की आवश्यकता नहीं है (न ही चाहते हैं), और भाषा डिजाइनर इसे हमसे छिपाने के लिए कड़ी मेहनत करते हैं। उदाहरण के लिए, C ++ में पास-बाय-रेफ़रेंस तर्क के बारे में सोचें। तो यह मेरे लिए आश्चर्य की बात नहीं है कि शुरुआती सभी जादू से भ्रमित होते हैं!
गयोम

जवाबों:


89

nullअसाइनमेंट विश्व स्तर पर उस ऑब्जेक्ट को नष्ट करके मूल्य नहीं बदलते हैं । इस तरह के व्यवहार से मुश्किल से लेकर बग और नकली व्यवहार तक ले जाया जा सकता है। वे केवल उस विशिष्ट संदर्भ को तोड़ते हैं ।

सादगी के लिए, आइए sampleबताते हैं कि 12345 पते की ओर इशारा करते हैं। यह शायद पता नहीं है, और इसका उपयोग केवल यहां चीजों को सरल बनाने के लिए किया जाता है। पता आमतौर पर अजीब हेक्साडेसिमल के साथ दिया जाता है Object#hashCode(), लेकिन यह कार्यान्वयन-निर्भर है। 1

StringBuilder sample = new StringBuilder(); //sample refers to 
//StringBuilder at 12345 

StringBuilder referToSample = sample; //referToSample refers to 
//the same StringBuilder at 12345 
//SEE DIAGRAM 1

referToSample = null; //referToSample NOW refers to 00000, 
//so accessing it will throw a NPE. 
//The other reference is not affected.
//SEE DIAGRAM 2

sample.append("A"); //sample STILL refers to the same StringBuilder at 12345 
System.out.println(sample);

See diagramउस समय की वस्तुओं के रेखाचित्रों के रूप में चिह्नित लाइनों से निम्नानुसार हैं:

चित्र 1:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]
                                                      
[StringBuilder referToSample] ------------------------/

चित्र 2:

[StringBuilder sample]    -----------------> [java.lang.StringBuilder@00012345]

[StringBuilder referToSample] ---->> [null pointer]

आरेख 2 से पता चलता है कि annulling StringBuilder referToSampleके संदर्भ को नहीं तोड़ता है ।sample00012345

1 जीसी विचार इस अनुमान को बनाते हैं।


@commit, अगर यह आपके प्रश्न का उत्तर देता है, तो कृपया ऊपर दिए गए पाठ के बाईं ओर वोट तीर के नीचे टिक पर क्लिक करें।
रे ब्रिटन

@RayBritton मुझे पसंद है कि लोग कैसे मान लेते हैं कि सबसे ज्यादा वोट देने वाला जवाब वही है जो इस सवाल का जवाब देता है। जबकि यह गिनती महत्वपूर्ण है कि यह केवल मीट्रिक नहीं है; -1 और -2 उत्तर सिर्फ इसलिए स्वीकार किए जा सकते हैं क्योंकि उन्होंने ओपी को सबसे ज्यादा मदद की।
नानोफारड

11
hashCode()है स्मृति पते के रूप में एक ही बात
केविन Panko

6
पहचान हैशकोड आमतौर पर ऑब्जेक्ट की मेमोरी लोकेशन से पहली बार विधि कहा जाता है। तब से उस हैशकोड को ठीक किया जाता है और याद किया जाता है भले ही मेमोरी मैनेजर ऑब्जेक्ट को किसी अलग मेमोरी लोकेशन पर ले जाने का फैसला करता है।
होल्गर

3
जो कक्षाएं ओवरराइड करती हैं, वे hashCodeआमतौर पर इसे ऐसे मान को लौटाने के लिए परिभाषित करती हैं, जिसका मेमोरी एड्रेस से कोई लेना-देना नहीं है। मुझे लगता है कि आप बिना उल्लेख के वस्तु संदर्भ बनाम वस्तुओं के बारे में अपनी बात रख सकते हैं hashCode। स्मृति पते को कैसे प्राप्त किया जाए इसके अच्छे बिंदुओं को दूसरे दिन के लिए छोड़ा जा सकता है।
केविन पैंको

62

प्रारंभ में जैसा कि आपने कहा referToSampleथा sampleकि नीचे दिखाया गया है:

1. परिदृश्य 1:

referToSample नमूना को संदर्भित करता है

2. परिदृश्य 1 (प्रतियोगिता):

referToSample.append ( "बी")

  • यहाँ जैसा referToSampleकि उल्लेख किया गया था sample, इसलिए आपने लिखते समय "बी" को जोड़ दिया

    referToSample.append("B")

परिदृश्य 2 में एक ही बात खुश :

लेकिन, 3. परिदृश्य 3 में: जैसा कि हेक्सफ़्रेक्शन ने कहा,

जब आप यह निर्दिष्ट nullकरते हैं referToSampleकि यह कब संदर्भित sampleकिया गया था तो इसके बदले मान नहीं बदला था क्योंकि यह केवल संदर्भ को तोड़ता है sample, और अब यह कहीं नहीं इंगित करता है । जैसा की नीचे दिखाया गया:

जब referToSample = null हो

अब, जैसा कि referToSampleबिंदु कहीं नहीं है , इसलिए जब तक आपके referToSample.append("A");पास कोई मान या संदर्भ नहीं होगा जहां वह ए। को जोड़ सकता है, तो वह फेंक देगा NullPointerException

BUT sampleअभी भी वैसा ही है जैसा आपने इसे इनिशियलाइज़ किया था

StringBuilder sample = new StringBuilder(); इसलिए इसे अयोग्य बना दिया गया है, इसलिए अब यह ए को जोड़ सकता है, और नहीं फेंकेगा NullPointerException


5
अच्छा चित्र। इसे दर्शाने में मदद करता है।
नानोफैड

अच्छा आरेख, लेकिन नीचे स्थित नोट 'अब संदर्भित होना चाहिए। संदर्भ referToSample = sample;नमूना "" का संदर्भ नहीं देता है क्योंकि जब आप नमूना का उल्लेख कर रहे हैं, तो आप केवल संदर्भित के पते की प्रतिलिपि बना रहे हैं
खालेद.के।

17

संक्षेप में: आप एक संदर्भ चर को अशक्त करते हैं, किसी वस्तु को नहीं।

एक उदाहरण में आप एक वस्तु की स्थिति को बदलते हैं जिसे दो संदर्भ चर द्वारा संदर्भित किया जाता है। जब ऐसा होता है, तो दोनों संदर्भ चर परिवर्तन को प्रतिबिंबित करेंगे।

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


तो अपने विशिष्ट "नियम" के अनुसार:

यदि दो वस्तुएं एक ही वस्तु का संदर्भ देती हैं तो यदि हम कोई मूल्य बदलते हैं तो यह दूसरे को भी प्रतिबिंबित करेगा क्योंकि दोनों एक ही स्मृति संदर्भ की ओर इशारा करते हैं।

फिर, आप उस एक वस्तु की स्थिति को बदलने का संदर्भ देते हैं जिसे दोनों चर संदर्भित करते हैं।

तो वह नियम यहां क्यों नहीं लागू हो रहा है? अगर मैं संदर्भित करने के लिए null निर्दिष्ट करता हूं तो नमूना भी अशक्त होना चाहिए और इसे nullPointerException फेंकना चाहिए लेकिन यह फेंक नहीं रहा है, क्यों?

फिर से, आप एक चर के संदर्भ को बदलते हैं जिसका दूसरे चर के संदर्भ पर कोई प्रभाव नहीं पड़ता है

ये दो पूरी तरह से अलग-अलग क्रियाएं हैं और दो अलग-अलग परिणाम होंगे।


3
@ कॉम: यह एक मूल लेकिन आलोचनात्मक अवधारणा है जो जावा के सभी को रेखांकित करती है और जिसे आप एक बार देखते हैं, आप कभी नहीं भूलेंगे।
भरा होवरक्राफ्ट

8

देखें यह सरल चित्र:

आरेख

जब आप किसी विधि को कॉल करते हैं referToSample, तब [your object]अपडेट किया जाता है, इसलिए यह sampleभी प्रभावित करता है। लेकिन जब आप कहते हैं referToSample = null, तो आप बस बदल रहे हैं जो referToSample संदर्भित करता है।


3

यहाँ 'नमूना' और 'referToSample' एक ही ऑब्जेक्ट को संदर्भित कर रहे हैं। यह एक ही मेमोरी लोकेशन पर पहुंचने वाले अलग-अलग पॉइंटर की अवधारणा है। तो अशक्त करने के लिए एक संदर्भ चर असाइन करना ऑब्जेक्ट को नष्ट नहीं करता है।

   referToSample = null;

मतलब 'referToSample' केवल शून्य की ओर इशारा करता है, ऑब्जेक्ट समान रहता है और अन्य संदर्भ चर ठीक काम कर रहे हैं। तो 'नमूना' के लिए जो अशक्त होने का संकेत नहीं करता है और एक वैध वस्तु है

   sample.append("A");

ठीक काम करता है। लेकिन अगर हम अशक्त को 'referToSample' में जोड़ने की कोशिश करते हैं, तो यह NullPointException को प्रदर्शित करेगा। अर्थात्,

   referToSample .append("A");-------> NullPointerException

यही कारण है कि आपको अपने तीसरे कोड स्निपेट में NullPointerException मिली।


0

जब भी किसी नए कीवर्ड का उपयोग किया जाता है तो वह हीप पर ऑब्जेक्ट बनाता है

1) StringBuilder नमूना = नया StringBuilder ();

2) StringBuilder referToSample = नमूना;

2 में) रेफरेंस का संदर्भ एक ही वस्तु के नमूने पर बनाया गया है

इस प्रकार referToSample = null; केवल Nulling है संदर्भनमूना संदर्भ नमूना का कोई प्रभाव नहीं दे रहा है यही कारण है कि आपको जावा के कचरा संग्रह के लिए NULL Pointer अपवाद नहीं मिल रहा है


0

बस आसान है, जावा के पास संदर्भ नहीं है, यह सिर्फ ऑब्जेक्ट संदर्भ है।


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