खोज…


टिप्पणियों

मान null एक फ़ील्ड का एक अनैतिक मूल्य के लिए डिफ़ॉल्ट मान है जिसका प्रकार एक संदर्भ प्रकार है।

NullPointerException (या NPE) अपवाद है जिसे तब फेंका जाता है जब आप null ऑब्जेक्ट संदर्भ पर अनुचित संचालन करने का प्रयास करते हैं। ऐसे कार्यों में शामिल हैं:

  • null लक्ष्य ऑब्जेक्ट पर इंस्टेंस विधि को कॉल करना,
  • एक null लक्ष्य वस्तु के एक क्षेत्र तक पहुँचने,
  • null वस्तु को अनुक्रमित करने या उसकी लंबाई तक पहुँचने का प्रयास करना,
  • एक synchronized ब्लॉक में म्यूटेक्स के रूप में एक null वस्तु संदर्भ का उपयोग करना,
  • एक null वस्तु संदर्भ डालना,
  • एक null वस्तु संदर्भ unboxing, और
  • एक null वस्तु संदर्भ फेंकना।

एनपीई के लिए सबसे आम मूल कारण:

  • एक संदर्भ प्रकार के साथ किसी क्षेत्र को आरम्भ करने की भूल करना,
  • किसी संदर्भ प्रकार के सरणी के तत्वों को आरंभीकृत करना भूल जाना, या
  • कुछ निश्चित परिस्थितियों में null लौटने के रूप में निर्दिष्ट कुछ एपीआई विधियों के परिणामों का परीक्षण नहीं कर रहा है।

आमतौर पर उपयोग किए जाने वाले तरीकों के उदाहरण जो null हैं:

  • Map एपीआई में get(key) विधि एक null लौटाएगा यदि आप इसे एक कुंजी के साथ कहते हैं जिसमें मैपिंग नहीं है।
  • getResource(path) और getResourceAsStream(path) मेथड्स ClassLoader और Class APIs यदि संसाधन नहीं मिल पा रहा है, तो null हो जाएगा।
  • यदि संदर्भ कलेक्टर ने संदर्भ को मंजूरी दे दी है, तो Reference API में get() विधि null हो जाएगी।
  • यदि आप एक गैर-मौजूद अनुरोध पैरामीटर, सत्र या सत्र विशेषता और इसी तरह लाने का प्रयास करते हैं, तो जावा ईई सर्वलेट एपीआई में विभिन्न getXxxx तरीके null हो जाएंगे।

अवांछित एनपीई से बचने के लिए रणनीतियाँ हैं, जैसे कि स्पष्ट रूप से null लिए परीक्षण या "योदा नोटेशन" का उपयोग करना, लेकिन इन रणनीतियों में अक्सर आपके कोड में समस्याओं को छिपाने का अवांछनीय परिणाम होता है जो वास्तव में ठीक होना चाहिए।

नुकसान - आदिम आवरणों के अनावश्यक उपयोग से NullPointerException हो सकती है

कभी-कभी, प्रोग्रामर जो नए जावा हैं, वे आदिम प्रकार और आवरणों का परस्पर उपयोग करेंगे। इससे समस्याएं हो सकती हैं। इस उदाहरण पर विचार करें:

public class MyRecord {
    public int a, b;
    public Integer c, d;
}

...
MyRecord record = new MyRecord();
record.a = 1;               // OK
record.b = record.b + 1;    // OK
record.c = 1;               // OK
record.d = record.d + 1;    // throws a NullPointerException

हमारा MyRecord वर्ग 1 अपने क्षेत्रों पर मूल्यों को आरंभ करने के लिए डिफ़ॉल्ट आरंभीकरण पर निर्भर करता है। इस प्रकार, जब हम एक new रिकॉर्ड करते हैं, तो a और b फ़ील्ड शून्य पर सेट हो जाएंगे, और c और d फ़ील्ड null सेट हो जाएंगे।

जब हम डिफ़ॉल्ट आरंभीकृत फ़ील्ड्स का उपयोग करने का प्रयास करते हैं, तो हम देखते हैं कि int फ़ील्ड्स हर समय काम करती हैं, लेकिन Integer फ़ील्ड कुछ मामलों में काम करते हैं और अन्य नहीं। विशेष रूप से, उस मामले में जो विफल होता है ( d साथ), ऐसा क्या होता है कि दाएं हाथ की तरफ अभिव्यक्ति एक null संदर्भ को अनबॉक्स करने का प्रयास करती है, और यही कारण है कि NullPointerException को फेंक दिया जाता है।

इसे देखने के कुछ तरीके हैं:

  • यदि फ़ील्ड c और d को आदिम आवरण होने की आवश्यकता है, तो या तो हमें डिफ़ॉल्ट आरंभीकरण पर निर्भर नहीं होना चाहिए, या हमें null लिए परीक्षण करना चाहिए। पूर्व के लिए सही दृष्टिकोण है जब तक कि null अवस्था में खेतों के लिए कोई निश्चित अर्थ नहीं है।

  • यदि खेतों को आदिम आवरण होने की आवश्यकता नहीं है, तो यह उन्हें आदिम आवरण बनाने के लिए एक गलती है। इस समस्या के अलावा, आदिम आवरणों में आदिम प्रकारों के सापेक्ष अतिरिक्त ओवरहेड्स होते हैं।

जब तक आपको वास्तव में ज़रूरत न हो तब तक सबक आदिम आवरण प्रकारों का उपयोग नहीं करना है।


1 - यह क्लास अच्छी कोडिंग प्रैक्टिस का उदाहरण नहीं है। उदाहरण के लिए, एक अच्छी तरह से डिजाइन वर्ग में सार्वजनिक क्षेत्र नहीं होंगे। हालाँकि, यह इस उदाहरण की बात नहीं है।

ख़तरा - रिक्त सरणी या संग्रह का प्रतिनिधित्व करने के लिए अशक्त का उपयोग करना

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

पहला संस्करण है कि आप सामान्य रूप से विधि को कैसे कोड करेंगे:

/**
 * Sum the values in an array of integers.
 * @arg values the array to be summed
 * @return the sum
 **/
public int sum(int[] values) {
    int sum = 0;
    for (int value : values) {
        sum += value;
    }
    return sum;
}

दूसरा संस्करण यह है कि यदि आपको खाली सरणी का प्रतिनिधित्व करने के लिए null का उपयोग करने की आदत है, तो आपको विधि को कोड करने की आवश्यकता है।

/**
 * Sum the values in an array of integers.
 * @arg values the array to be summed, or null.
 * @return the sum, or zero if the array is null.
 **/
public int sum(int[] values) {
    int sum = 0;
    if (values != null) {
        for (int value : values) {
            sum += value;
        }
    }
    return sum;
}

जैसा कि आप देख सकते हैं, कोड थोड़ा अधिक जटिल है। इस तरह से null उपयोग करने के निर्णय के लिए यह सीधे जिम्मेदार है।

अब विचार करें कि क्या यह सरणी जो एक null हो सकती है, बहुत सारे स्थानों पर उपयोग की जाती है। प्रत्येक स्थान पर जहां आप इसका उपयोग करते हैं, आपको यह विचार करने की आवश्यकता है कि क्या आपको null लिए परीक्षण करने की आवश्यकता है। यदि आपको एक null परीक्षा याद आती है, जिसमें आपको होने की आवश्यकता है, तो आप NullPointerException जोखिम में डालते हैं। इसलिए, इस तरह से null उपयोग करने की रणनीति आपके आवेदन को अधिक नाजुक बनाती है; प्रोग्रामर त्रुटियों के परिणामों के लिए अधिक असुरक्षित है।


यहाँ सबक खाली सरणियों और खाली सूचियों का उपयोग करना है जब कि आपका मतलब है।

int[] values = new int[0];                     // always empty
List<Integer> list = new ArrayList();          // initially empty
List<Integer> list = Collections.emptyList();  // always empty

अंतरिक्ष ओवरहेड छोटा है, और इसे कम करने के अन्य तरीके हैं यदि यह करने के लिए एक सार्थक चीज है।

नुकसान - "अच्छा बनाना" अप्रत्याशित नल

StackOverflow पर, हम अक्सर उत्तर में इस तरह कोड देखते हैं:

public String joinStrings(String a, String b) {
    if (a == null) {
        a = "";
    }
    if (b == null) {
        b = "";
    }
    return a + ": " + b;
}

अक्सर, यह एक मुखरता के साथ होता है जो NullPointerException से बचने के लिए इस तरह null लिए परीक्षण करने के लिए "सर्वोत्तम अभ्यास" है।

क्या यह सबसे अच्छा अभ्यास है? संक्षेप में: नहीं।

कुछ अंतर्निहित धारणाएं हैं, joinStrings हमें यह कहने से पहले सवाल करने की जरूरत है कि क्या हमारे joinStrings में ऐसा करना एक अच्छा विचार है:

"ए" या "बी" के अशक्त होने का क्या मतलब है?

एक String मान शून्य या अधिक वर्ण हो सकता है, इसलिए हमारे पास पहले से ही एक खाली स्ट्रिंग का प्रतिनिधित्व करने का एक तरीका है। क्या null मतलब कुछ अलग है "" ? यदि नहीं, तो खाली स्ट्रिंग का प्रतिनिधित्व करने के दो तरीके होना समस्याग्रस्त है।

क्या अशक्त एक असिंचित चर से आया था?

null एक असिंचित क्षेत्र, या एक असिंचित सरणी तत्व से आ सकता है। मूल्य डिजाइन द्वारा, या दुर्घटना से निर्विवाद हो सकता है। यदि यह दुर्घटना से होता है तो यह एक बग है।

क्या अशक्त "नहीं जानता" या "लापता मूल्य" का प्रतिनिधित्व करता है?

कभी-कभी एक null वास्तविक अर्थ हो सकता है; जैसे कि एक चर का वास्तविक मूल्य अज्ञात या अनुपलब्ध या "वैकल्पिक" है। जावा 8 में, Optional वर्ग इसे व्यक्त करने का एक बेहतर तरीका प्रदान करता है।

यदि यह एक बग (या एक डिज़ाइन त्रुटि) है तो क्या हमें "अच्छा बनाना चाहिए"?

कोड की एक व्याख्या यह है कि हम इसके स्थान पर एक खाली स्ट्रिंग का उपयोग करके एक अप्रत्याशित null को "अच्छा बना रहे हैं"। क्या सही रणनीति है? क्या NullPointerException होने देना बेहतर होगा, और फिर अपवाद को स्टैक के ऊपर से पकड़कर बग के रूप में लॉग इन करें?

"अच्छा बनाने" के साथ समस्या यह है कि यह या तो समस्या को छिपाने के लिए उत्तरदायी है, या इसका निदान करना कठिन है।

क्या यह कोड गुणवत्ता के लिए कुशल / अच्छा है?

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

संक्षेप में

यदि null एक सार्थक मूल्य है, तो null मामले के लिए परीक्षण सही दृष्टिकोण है। कोरोलरी यह है कि यदि कोई null मान सार्थक है, तो यह स्पष्ट रूप से किसी भी तरीके के javadocs में दस्तावेज होना चाहिए जो null मान को स्वीकार करते हैं या इसे वापस करते हैं।

अन्यथा, यह एक बेहतर विचार एक अप्रत्याशित इलाज के लिए null एक प्रोग्रामिंग त्रुटि के रूप में, और NullPointerException हो ताकि डेवलपर पता करने के लिए वहाँ कोड में एक समस्या है हो जाता है।

नुकसान - एक अपवाद को फेंकने के बजाय अशक्त लौटना

कुछ जावा प्रोग्रामर अपवादों को फेंकने या प्रचार करने के लिए एक सामान्य विरोधाभास रखते हैं। यह निम्नलिखित की तरह कोड की ओर जाता है:

public Reader getReader(String pathname) {
    try {
        return new BufferedReader(FileReader(pathname));
    } catch (IOException ex) {
        System.out.println("Open failed: " + ex.getMessage());
        return null;
    }

}

तो इसमें समस्या क्या है?

समस्या यह है कि getReader एक विशेष मान के रूप में एक null वापसी कर रहा है ताकि यह इंगित किया जा सके कि Reader को खोला नहीं जा सका। अब दिए गए मान को यह देखने के लिए परीक्षण करने की आवश्यकता है कि क्या यह उपयोग करने से पहले null है। यदि परीक्षण छोड़ दिया जाता है, तो परिणाम NullPointerException

यहाँ वास्तव में तीन समस्याएं हैं:

  1. IOException को बहुत जल्द पकड़ा गया था।
  2. इस कोड की संरचना का मतलब है कि एक संसाधन को लीक करने का जोखिम है।
  3. एक null का उपयोग किया गया था, क्योंकि कोई "वास्तविक" Reader वापस आने के लिए उपलब्ध नहीं था।

वास्तव में, यह मानते हुए कि अपवाद को इस तरह जल्दी पकड़ने की आवश्यकता थी, null लौटने के लिए कुछ विकल्प थे:

  1. NullReader वर्ग को लागू करना संभव होगा; उदाहरण के लिए, जहां एपीआई का संचालन व्यवहार करता है जैसे कि पाठक "फाइल के अंत" स्थिति में पहले से ही था।
  2. Java 8 के साथ, getReader को एक Optional<Reader> रूप में लौटाने की घोषणा करना संभव होगा।

नुकसान - अगर I / O स्ट्रीम को बंद करते समय इसे इनिशियलाइज़ नहीं किया गया है, तो भी चेक न करें

मेमोरी लीक को रोकने के लिए, किसी को इनपुट स्ट्रीम या आउटपुट स्ट्रीम को बंद नहीं करना चाहिए, जिसका काम किया जाता है। यह आमतौर पर एक try साथ किया जाता है - catch - finally में catch भाग के बिना स्टेटमेंट:

void writeNullBytesToAFile(int count, String filename) throws IOException {
    FileOutputStream out = null;
    try {
        out = new FileOutputStream(filename);
        for(; count > 0; count--)
            out.write(0);
    } finally {
        out.close();
    }
}

जबकि उपरोक्त कोड निर्दोष दिख सकता है, इसमें एक दोष है जो डिबगिंग को असंभव बना सकता है। लाइन जहां तो out (आरंभ नहीं हो जाता out = new FileOutputStream(filename) ) एक अपवाद फेंकता है, तो out हो जाएगा null जब out.close() , निष्पादित किया जाता है एक बुरा है, जिसके परिणामस्वरूप NullPointerException !

इसे रोकने के लिए, यह सुनिश्चित करने के लिए कि इसे बंद करने की कोशिश करने से पहले धारा null नहीं है।

void writeNullBytesToAFile(int count, String filename) throws IOException {
    FileOutputStream out = null;
    try {
        out = new FileOutputStream(filename);
        for(; count > 0; count--)
            out.write(0);
    } finally {
        if (out != null)
            out.close();
    }
}

इससे भी बेहतर तरीका है, try -संसाधनों के बाद से, यह स्वतः 0 के प्रायिकता के साथ स्ट्रीम को बंद कर देगा क्योंकि NPE को finally ब्लॉक की आवश्यकता के बिना फेंकने के लिए।

void writeNullBytesToAFile(int count, String filename) throws IOException {
    try (FileOutputStream out = new FileOutputStream(filename)) {
        for(; count > 0; count--)
            out.write(0);
    }
}

नुकसान - NullPointerException से बचने के लिए "Yoda संकेतन" का उपयोग करना

StackOverflow पर पोस्ट किए गए बहुत सारे उदाहरण कोड में इस तरह के स्निपेट शामिल हैं:

if ("A".equals(someString)) {
    // do something
}

यह उस मामले में एक संभव NullPointerException को "रोकना" या "टालना" करता है जो कुछ someString null । इसके अलावा, यह तर्क है कि

    "A".equals(someString)

से बेहतर है:

    someString != null && someString.equals("A")

(यह अधिक संक्षिप्त है, और कुछ परिस्थितियों में यह अधिक कुशल हो सकता है। हालांकि, जैसा कि हम नीचे तर्क देते हैं, संक्षिप्तता एक नकारात्मक हो सकती है।)

हालाँकि, असली नुकसान Yoda परीक्षण का उपयोग NullPointerExceptions को आदत के मामले से बचने के लिए किया जाता है।

जब आप "A".equals(someString) आप वास्तव में "अच्छा बना रहे हैं" उस मामले में जहां कुछ someString null होता है। लेकिन एक अन्य उदाहरण के रूप में ( ख़तरा - "अच्छा बनाना" अप्रत्याशित नल ) बताते हैं, "अच्छा" null मान कई कारणों से हानिकारक हो सकता है।

इसका मतलब है कि योदा की स्थिति "सबसे अच्छा अभ्यास" 1 नहीं हैं। जब तक null होने की उम्मीद है, तब तक बेहतर है कि NullPointerException ऐसा करने दिया जाए ताकि आपको एक इकाई परीक्षण विफलता (या बग रिपोर्ट) मिल सके। यह आपको उस बग को खोजने और ठीक करने की अनुमति देता है जिससे अप्रत्याशित / अवांछित null दिखाई देता है।

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


1 - विकिपीडिया के अनुसार: "सर्वश्रेष्ठ कोडिंग प्रथाएं अनौपचारिक नियमों का एक सेट है जो सॉफ्टवेयर विकास समुदाय ने समय के साथ सीखा है जो सॉफ्टवेयर की गुणवत्ता में सुधार करने में मदद कर सकता है।" । योदा अंकन का उपयोग करने से यह हासिल नहीं होता है। बहुत सारी स्थितियों में, यह कोड को बदतर बनाता है।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow