खोज…


परिचय

यह विषय जावा में शुरुआती लोगों द्वारा की गई कुछ सामान्य गलतियों को रेखांकित करता है।

इसमें जावा भाषा के उपयोग या रन-टाइम वातावरण की समझ में कोई आम गलतियाँ शामिल हैं।

विशिष्ट API से संबंधित गलतियाँ उन API के लिए विशिष्ट विषयों में वर्णित की जा सकती हैं। स्ट्रिंग्स एक विशेष मामला है; वे जावा भाषा विनिर्देश में शामिल हैं। स्ट्रिंग्स पर इस विषय में सामान्य गलतियों के अलावा अन्य विवरणों का वर्णन किया जा सकता है।

नुकसान: == का उपयोग करके आदिम आवरण ऑब्जेक्ट्स जैसे कि इंटीगर की तुलना करना

(यह नुकसान सभी आदिम आवरण प्रकारों पर समान रूप से लागू होता है, लेकिन हम इसे Integer और int लिए स्पष्ट करेंगे।)

जब Integer वस्तुओं के साथ काम करते हैं, तो मूल्यों की तुलना करने के लिए == का उपयोग करना ललचाता है, क्योंकि यही आप int मूल्यों के साथ करेंगे। और कुछ मामलों में यह काम करने लगेगा:

Integer int1_1 = Integer.valueOf("1");
Integer int1_2 = Integer.valueOf(1);

System.out.println("int1_1 == int1_2: " + (int1_1 == int1_2));          // true
System.out.println("int1_1 equals int1_2: " + int1_1.equals(int1_2));   // true

यहां हमने 1 साथ दो Integer ऑब्जेक्ट बनाए और उनकी तुलना की (इस मामले में हमने एक String और एक int शाब्दिक से बनाया। अन्य विकल्प भी हैं)। इसके अलावा, हम मानते हैं कि दो तुलना के तरीके ( == और equals ) दोनों उपज true

जब हम अलग-अलग मान चुनते हैं तो यह व्यवहार बदल जाता है:

Integer int2_1 = Integer.valueOf("1000");
Integer int2_2 = Integer.valueOf(1000);

System.out.println("int2_1 == int2_2: " + (int2_1 == int2_2));          // false
System.out.println("int2_1 equals int2_2: " + int2_1.equals(int2_2));   // true

इस मामले में, केवल equals तुलना सही परिणाम देती है।

व्यवहार में इस अंतर का कारण, यह है कि JVM, -128 से 127 तक सीमा के लिए Integer वस्तुओं का कैश रखता है। (ऊपरी मूल्य सिस्टम की संपत्ति "java.lang.Integer.IntegerCache.high" या के साथ ओवरराइड किया जा सकता है) JVM तर्क "-XX: AutoBoxCacheMax = size")। इस श्रेणी के मानों के लिए, Integer.valueOf() नया बनाने के बजाय कैश किया गया मान लौटाएगा।

इस प्रकार, पहले उदाहरण में Integer.valueOf(1) और Integer.valueOf("1") कॉल ने एक ही कैश्ड Integer आवृत्ति लौटा दी। इसके विपरीत, दूसरे उदाहरण में Integer.valueOf(1000) और Integer.valueOf("1000") दोनों ने Integer नए Integer ऑब्जेक्ट बनाए और वापस लौटाए।

== संदर्भ समानता (यानी एक ही वस्तु) के लिए संदर्भ प्रकार के परीक्षण के लिए ऑपरेटर। इसलिए, पहले उदाहरण में int1_1 == int1_2 true क्योंकि संदर्भ समान हैं। दूसरे उदाहरण में int2_1 == int2_2 गलत है क्योंकि संदर्भ भिन्न हैं।

नुकसान: मुक्त संसाधनों को भूल जाना

जब भी कोई प्रोग्राम किसी संसाधन को खोलता है, जैसे कि फ़ाइल या नेटवर्क कनेक्शन, संसाधन का उपयोग करने के बाद उसे मुक्त करना महत्वपूर्ण है। यदि इस तरह के संसाधनों पर संचालन के दौरान किसी भी अपवाद को फेंक दिया गया था, तो इसी तरह की सावधानी बरती जानी चाहिए। एक तर्क दे सकता है कि FileInputStream पास एक अंतिम उपकरण है जो एक कचरा संग्रहण घटना पर close() विधि को लागू करता है; हालाँकि, जब से हम यह सुनिश्चित नहीं कर सकते कि कचरा संग्रहण चक्र कब शुरू होगा, इनपुट स्ट्रीम कंप्यूटर संसाधनों का अनिश्चित समय के लिए उपभोग कर सकती है। संसाधन को finally में पकड़ने वाले ब्लॉक के खंड में बंद होना चाहिए:

जावा एसई 7
private static void printFileJava6() throws IOException {
    FileInputStream input;
    try {
        input = new FileInputStream("file.txt");
        int data = input.read();
        while (data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    } finally {
        if (input != null) {
            input.close();
        }
    }
}

जावा 7 के बाद से जावा 7 में विशेष रूप से इस मामले के लिए एक बहुत ही उपयोगी और साफ-सुथरा वक्तव्य प्रस्तुत किया गया है, जिसे प्रयास-साथ-संसाधन कहा जाता है:

जावा एसई 7
private static void printFileJava7() throws IOException {
    try (FileInputStream input = new FileInputStream("file.txt")) {
        int data = input.read();
        while (data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }
}

कोशिश के साथ-संसाधनों बयान किसी भी वस्तु है कि औजार के साथ इस्तेमाल किया जा सकता Closeable या AutoCloseable इंटरफ़ेस। यह सुनिश्चित करता है कि प्रत्येक संसाधन विवरण के अंत तक बंद है। दो इंटरफेस के बीच का अंतर यह है कि Closeable का close() तरीका एक IOException फेंकता है जिसे किसी तरह से हैंडल करना होता है।

उन मामलों में जहां संसाधन पहले से ही खोला जा चुका है, लेकिन उपयोग के बाद सुरक्षित रूप से बंद होना चाहिए, कोई इसे कोशिश-के-संसाधनों के अंदर एक स्थानीय चर को सौंप सकता है

जावा एसई 7
private static void printFileJava7(InputStream extResource) throws IOException {
    try (InputStream input = extResource) {
        ... //access resource
    }
}

कोशिश-के साथ संसाधनों के निर्माण में बनाया गया स्थानीय संसाधन चर प्रभावी रूप से अंतिम है।

नुकसान: स्मृति लीक

जावा स्वचालित रूप से मेमोरी का प्रबंधन करता है। आपको मैन्युअल रूप से मेमोरी खाली करने की आवश्यकता नहीं है। ढेर पर एक ऑब्जेक्ट की मेमोरी को एक कचरा कलेक्टर द्वारा मुक्त किया जा सकता है जब ऑब्जेक्ट अब एक जीवित थ्रेड द्वारा पहुंच योग्य नहीं है।

हालाँकि, आप स्मृति को मुक्त होने से रोक सकते हैं, वस्तुओं को पुनः प्राप्त करने की अनुमति देकर जिन्हें अब आवश्यकता नहीं है। चाहे आप इसे मेमोरी लीक कहें या मेमोरी पैकिंग, परिणाम एक ही है - आवंटित मेमोरी में अनावश्यक वृद्धि।

जावा में मेमोरी लीक विभिन्न तरीकों से हो सकता है, लेकिन सबसे आम कारण हमेशा के लिए संदर्भ वस्तु है, क्योंकि कचरा संग्रहकर्ता ढेर से वस्तुओं को नहीं हटा सकता है, जबकि अभी भी उनके संदर्भ हैं।

स्थैतिक क्षेत्र

कोई एक static फ़ील्ड के साथ वस्तुओं के कुछ संग्रह के साथ वर्ग को परिभाषित करके ऐसा संदर्भ बना सकता है, और संग्रह के बाद उस static क्षेत्र को null करने के लिए सेट करने की ज़रूरत नहीं है। static क्षेत्रों को जीसी जड़ माना जाता है और कभी भी एकत्र नहीं किया जाता है। जब जेएनआई का उपयोग किया जाता है तो एक और मुद्दा गैर-ढेर स्मृति में लीक होता है।

कक्षा लोडर

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

संचय रिसाव संचय रिसाव उदाहरण निम्न की तरह लग सकता है:

final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
final Deque<BigDecimal> numbers = new LinkedBlockingDeque<>();
final BigDecimal divisor = new BigDecimal(51);

scheduledExecutorService.scheduleAtFixedRate(() -> {
    BigDecimal number = numbers.peekLast();
    if (number != null && number.remainder(divisor).byteValue() == 0) {
        System.out.println("Number: " + number);
        System.out.println("Deque size: " + numbers.size());
    }
}, 10, 10, TimeUnit.MILLISECONDS);

scheduledExecutorService.scheduleAtFixedRate(() -> {
    numbers.add(new BigDecimal(System.currentTimeMillis()));
}, 10, 10, TimeUnit.MILLISECONDS);

try {
    scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
    e.printStackTrace();
}

यह उदाहरण दो अनुसूचित कार्य बनाता है। पहला कार्य अंतिम संख्या को एक संख्या से हटाता है जिसे numbers कहा जाता है, और, यदि संख्या 51 से विभाज्य है, तो यह संख्या और छंद के आकार को प्रिंट करती है। दूसरा कार्य संख्या को छल में डालता है। दोनों कार्यों को एक निश्चित दर पर निर्धारित किया गया है, और वे प्रत्येक 10 एमएस चलाते हैं।

यदि कोड निष्पादित होता है, तो आप देखेंगे कि deque का आकार स्थायी रूप से बढ़ रहा है। यह अंततः सभी उपलब्ध हीप मेमोरी का उपभोग करने वाली वस्तुओं से भरे जाने के लिए deque का कारण होगा।

इस कार्यक्रम के शब्दार्थों को संरक्षित करते हुए इसे रोकने के लिए, हम एक अलग विधि का उपयोग कर सकते हैं: pollLast से अंक लेने के लिए: pollLast । पद्धति के विपरीत peekLast , pollLast तत्व को वापस कर देता है और इसे हटाता है जबकि peekLast केवल अंतिम तत्व देता है।

नुकसान: स्ट्रिंग की तुलना करने के लिए == का उपयोग करना

जावा शुरुआत करने वालों के लिए एक सामान्य गलती यह है कि दो तार समान होने पर परीक्षण करने के लिए == ऑपरेटर का उपयोग करें। उदाहरण के लिए:

public class Hello {
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0] == "hello") {
                System.out.println("Hello back to you");
            } else {
                System.out.println("Are you feeling grumpy today?");
            }
        }
    }
}

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

इस विशेष मामले में String "हैलो" स्ट्रिंग पूल में डाल दिया जाता है जबकि String ढेर [0] हीप पर रहता है। इसका मतलब है कि एक ही शाब्दिक का प्रतिनिधित्व करने वाली दो वस्तुएं हैं, प्रत्येक इसके संदर्भ के साथ हैं। चूँकि == संदर्भों के लिए परीक्षण, वास्तविक समानता नहीं है, इसलिए तुलना सबसे अधिक बार झूठी होगी। इसका मतलब यह नहीं है कि यह हमेशा ऐसा करेगा।

जब आप स्ट्रिंग का परीक्षण करने के लिए == का उपयोग करते हैं, तो आप वास्तव में क्या परीक्षण कर रहे हैं यदि दो String ऑब्जेक्ट एक ही जावा ऑब्जेक्ट हैं। दुर्भाग्य से, यह जावा में स्ट्रिंग समानता का मतलब नहीं है। वास्तव में, स्ट्रिंग्स का परीक्षण करने का सही तरीका equals(Object) विधि का उपयोग करना है। तार की एक जोड़ी के लिए, हम आमतौर पर परीक्षण करना चाहते हैं कि क्या वे एक ही क्रम में एक ही वर्ण से मिलकर होते हैं।

public class Hello2 {
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0].equals("hello")) {
                System.out.println("Hello back to you");
            } else {
                System.out.println("Are you feeling grumpy today?");
            }
        }
    }
}

लेकिन यह वास्तव में खराब हो जाता है। समस्या यह है कि == कुछ परिस्थितियों में अपेक्षित उत्तर देगा । उदाहरण के लिए

public class Test1 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        if (s1 == s2) {
            System.out.println("same");
        } else {
            System.out.println("different");
        }
    }
}

दिलचस्प बात यह है कि यह "एक ही" प्रिंट करेगा, भले ही हम तारों का गलत तरीके से परीक्षण कर रहे हों। ऐसा क्यों है? क्योंकि जावा लैंग्वेज स्पेसिफिकेशन (धारा 3.10.5: स्ट्रिंग लिटरल्स) यह निर्धारित करता है कि कोई भी दो स्ट्रिंग >> शाब्दिक << समान वर्णों से मिलकर वास्तव में उसी जावा ऑब्जेक्ट द्वारा दर्शाए जाएंगे। इसलिए, == परीक्षण समान शाब्दिकों के लिए सही होगा। (जब आपका कोड लोड किया जाता है, तो स्ट्रिंग लिटरल्स को "इंटर्न" किया जाता है और एक साझा "स्ट्रिंग पूल" में जोड़ा जाता है, लेकिन यह एक कार्यान्वयन विवरण है।)

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

    public class Test1 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hel" + "lo";
        String s3 = " mum";
        if (s1 == s2) {
            System.out.println("1. same");
        } else {
            System.out.println("1. different");
        }
        if (s1 + s3 == "hello mum") {
            System.out.println("2. same");
        } else {
            System.out.println("2. different");
        }
    }
}

यह "1. वही" और "2. अलग" आउटपुट देगा। पहले मामले में, संकलित समय पर + अभिव्यक्ति का मूल्यांकन किया जाता है और हम एक String ऑब्जेक्ट की खुद से तुलना करते हैं। दूसरे मामले में, रन टाइम पर इसका मूल्यांकन किया जाता है और हम दो अलग-अलग String वस्तुओं की तुलना करते हैं

सारांश में, जावा में स्ट्रिंग का परीक्षण करने के लिए == का उपयोग करना लगभग हमेशा गलत होता है, लेकिन गलत उत्तर देने की गारंटी नहीं है।

नुकसान: इसे खोलने के प्रयास से पहले किसी फ़ाइल का परीक्षण करना।

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

public static File getValidatedFile(String path) throws IOException {
    File f = new File(path);
    if (!f.exists()) throw new IOException("Error: not found: " + path);
    if (!f.isFile()) throw new IOException("Error: Is a directory: " + path);
    if (!f.canRead()) throw new IOException("Error: cannot read file: " + path);
    return f;
}

आप इस तरह उपरोक्त विधि का उपयोग कर सकते हैं:

File f = null;
try {
    f = getValidatedFile("somefile");
} catch (IOException ex) {
    System.err.println(ex.getMessage());
    return;
}
try (InputStream is = new FileInputStream(file)) {
    // Read data etc.
}

पहली समस्या FileInputStream(File) लिए हस्ताक्षर में है क्योंकि कंपाइलर अभी भी आग्रह करेगा कि हम यहां IOException को IOException , या स्टैक को आगे IOException

दूसरी समस्या यह है कि getValidatedFile द्वारा किए गए चेक इस बात की गारंटी नहीं देते हैं कि FileInputStream सफल होगा।

  • दौड़ की स्थिति: एक और धागा या एक अलग प्रक्रिया फ़ाइल का नाम बदल सकती है, फ़ाइल को हटा सकती है, या getValidatedFile रिटर्न के बाद रीड एक्सेस को हटा getValidatedFile है। यह कस्टम संदेश के बिना एक "सादे" IOException को IOException

  • उन परीक्षणों द्वारा कवर नहीं किए गए किनारे मामले हैं। उदाहरण के लिए, " canRead() " मोड में SELinux के साथ एक सिस्टम पर, फ़ाइल को पढ़ने का प्रयास विफल होने के बावजूद विफल हो सकता है canRead() true लौटने पर।

तीसरी समस्या यह है कि परीक्षण अक्षम हैं। उदाहरण के लिए, exists , isFile और canRead कॉल्स हैं, प्रत्येक आवश्यक जांच करने के लिए एक syscall बना देगा। एक और syscall तब फ़ाइल को खोलने के लिए बनाया गया है, जो पर्दे के पीछे एक ही चेक को दोहराता है।

संक्षेप में, getValidatedFile जैसे तरीकों को गुमराह किया जाता है। केवल फ़ाइल को खोलने और अपवाद को संभालने का प्रयास करना बेहतर है:

try (InputStream is = new FileInputStream("somefile")) {
    // Read data etc.
} catch (IOException ex) {
    System.err.println("IO Error processing 'somefile': " + ex.getMessage());
    return;
}

यदि आप खोलने और पढ़ने के दौरान फेंकी गई आईओ त्रुटियों को अलग करना चाहते हैं, तो आप एक नेस्टेड कोशिश / कैच का उपयोग कर सकते हैं। यदि आप खुली विफलताओं के लिए बेहतर डायग्नोस्टिक्स का उत्पादन करना चाहते हैं, तो आप हैंडलर में exists , isFile और canRead चेक कर सकते हैं।

नुकसान: वस्तुओं के रूप में चर की सोच

कोई जावा चर किसी वस्तु का प्रतिनिधित्व नहीं करता है।

String foo;   // NOT AN OBJECT

किसी भी जावा सरणी में ऑब्जेक्ट नहीं हैं।

String bar[] = new String[100];  // No member is an object.

यदि आप गलती से वस्तुओं के रूप में चर के बारे में सोचते हैं, तो जावा भाषा का वास्तविक व्यवहार आपको आश्चर्यचकित करेगा।

  • जावा चर के लिए जो एक आदिम प्रकार (जैसे int या float ) है, चर मूल्य की एक प्रति रखता है। एक आदिम मूल्य की सभी प्रतियां अप्रभेद्य हैं; यानी नंबर एक के लिए केवल एक int वैल्यू है। आदिम मूल्य वस्तु नहीं हैं और वे वस्तुओं की तरह व्यवहार नहीं करते हैं।

  • जावा चर के लिए, जिसमें एक संदर्भ प्रकार (या तो एक वर्ग या एक सरणी प्रकार) है, चर एक संदर्भ रखता है। एक संदर्भ की सभी प्रतियां अप्रभेद्य हैं। संदर्भ वस्तुओं की ओर इशारा कर सकते हैं, या वे null हो सकते हैं जिसका अर्थ है कि वे किसी वस्तु की ओर इशारा करते हैं। हालाँकि, वे वस्तु नहीं हैं और वे वस्तुओं की तरह व्यवहार नहीं करते हैं।

चर या तो मामले में ऑब्जेक्ट नहीं हैं, और वे किसी भी मामले में ऑब्जेक्ट नहीं होते हैं। उनमें वस्तुओं का संदर्भ हो सकता है, लेकिन वह कुछ अलग कह रही है।

उदाहरण वर्ग

इस वर्ग का उपयोग करने वाले उदाहरण 2 डी अंतरिक्ष में एक बिंदु का प्रतिनिधित्व करते हैं।

public final class MutableLocation {
   public int x;
   public int y;

   public MutableLocation(int x, int y) {
       this.x = x;
       this.y = y;
   }

   public boolean equals(Object other) {
       if (!(other instanceof MutableLocation) {
           return false;
       }
       MutableLocation that = (MutableLocation) other;
       return this.x == that.x && this.y == that.y;
   }
}

इस वर्ग का एक उदाहरण एक ऐसी वस्तु है जिसमें दो फ़ील्ड्स x और y होते हैं जिनका प्रकार int

हमारे पास MutableLocation वर्ग के कई उदाहरण हो सकते हैं। कुछ 2 डी अंतरिक्ष में समान स्थानों का प्रतिनिधित्व करेंगे; यानी x और y के संबंधित मूल्य मेल खाएँगे। अन्य विभिन्न स्थानों का प्रतिनिधित्व करेंगे।

एकाधिक चर एक ही वस्तु को इंगित कर सकते हैं

 MutableLocation here = new MutableLocation(1, 2);
 MutableLocation there = here;
 MutableLocation elsewhere = new MutableLocation(1, 2);

उपर्युक्त में, हमने तीन चर here , there और elsewhere पर घोषित किए elsewhere जो MutableLocation वस्तुओं के संदर्भ में पकड़ सकते हैं।

यदि आप (गलत तरीके से) इन चरों को वस्तु मानते हैं, तो आपको कथन के गलत होने की संभावना है:

  1. यहां "[1, 2]" स्थान कॉपी here
  2. स्थान कॉपी करें "[1, 2]" करने के लिए there
  3. स्थान "[1, 2]" को elsewhere कॉपी करें

उससे, आपको यह अनुमान लगाने की संभावना है कि हमारे पास तीन चर में तीन स्वतंत्र वस्तुएं हैं। वास्तव में उपरोक्त द्वारा बनाई गई केवल दो वस्तुएं हैं। चर here और there वास्तव में एक ही वस्तु को देखें।

हम इसका प्रदर्शन कर सकते हैं। उपरोक्त के रूप में चर घोषणाओं को मानते हुए:

System.out.println("BEFORE: here.x is " + here.x + ", there.x is " + there.x +
                   "elsewhere.x is " + elsewhere.x);
here.x = 42;
System.out.println("AFTER: here.x is " + here.x + ", there.x is " + there.x +
                   "elsewhere.x is " + elsewhere.x);

यह निम्नलिखित उत्पादन करेगा:

BEFORE: here.x is 1, there.x is 1, elsewhere.x is 1
AFTER: here.x is 42, there.x is 42, elsewhere.x is 1

हमने यहां एक नया मान दिया है। here.x और इसने उस मान को बदल दिया जो हम there.x देखते हैं। there.x वे उसी वस्तु का उल्लेख कर रहे हैं। लेकिन जो मूल्य हम elsewhere.x देखते elsewhere.x वह नहीं बदला है, इसलिए elsewhere और एक अलग वस्तु को संदर्भित करना चाहिए।

यदि एक चर एक वस्तु थी, तो यहाँ असाइनमेंट। here.x = 42 नहीं बदलेगा। there.x

समानता ऑपरेटर यह परीक्षण नहीं करता है कि दो वस्तुएं समान हैं

यदि मान समान ऑब्जेक्ट को संदर्भित करते हैं, तो संदर्भ मान परीक्षण के लिए समानता ( == ) ऑपरेटर को लागू करना। यह परीक्षण नहीं करता है कि क्या दो (अलग) ऑब्जेक्ट सहज अर्थ में "बराबर" हैं।

 MutableLocation here = new MutableLocation(1, 2);
 MutableLocation there = here;
 MutableLocation elsewhere = new MutableLocation(1, 2);

 if (here == there) {
     System.out.println("here is there");
 }
 if (here == elsewhere) {
     System.out.println("here is elsewhere");
 }

यह "यहां है" प्रिंट करेगा, लेकिन यह "यहां कहीं और नहीं है" प्रिंट नहीं करेगा। ( here और elsewhere संदर्भ दो अलग-अलग वस्तुओं के लिए हैं।)

इसके विपरीत, अगर हम equals(Object) विधि को कहते हैं, जिसे हमने ऊपर लागू किया था, तो हम परीक्षण करने जा रहे हैं यदि दो MutableLocation उदाहरणों में एक समान स्थान है।

 if (here.equals(there)) {
     System.out.println("here equals there");
 }
 if (here.equals(elsewhere)) {
     System.out.println("here equals elsewhere");
 }

इससे दोनों संदेश छपेंगे। विशेष रूप से, यहाँ। here.equals(elsewhere) true क्योंकि हमने दो MutableLocation ऑब्जेक्ट्स की समानता के लिए जो शब्दार्थ मापदंड चुना है, वह संतुष्ट है।

मेथड कॉल ऑब्जेक्ट्स को पास नहीं करती हैं

जावा विधि कॉल तर्क पास करने और परिणाम वापस करने के लिए मान 1 से पास का उपयोग करता है।

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

जब तक दोनों ऑब्जेक्ट संदर्भ अभी भी एक ही ऑब्जेक्ट की ओर इशारा कर रहे हैं, आप उस ऑब्जेक्ट को या तो संदर्भ से संशोधित कर सकते हैं, और यही कारण है कि कुछ के लिए भ्रम की स्थिति है।

हालाँकि, आप संदर्भ 2 द्वारा ऑब्जेक्ट पास नहीं कर रहे हैं। भेद यह है कि यदि ऑब्जेक्ट रेफरेंस कॉपी को किसी अन्य ऑब्जेक्ट को इंगित करने के लिए संशोधित किया जाता है, तो मूल ऑब्जेक्ट संदर्भ अभी भी मूल ऑब्जेक्ट को इंगित करेगा।

void f(MutableLocation foo) {  
    foo = new MutableLocation(3, 4);   // Point local foo at a different object.
}

void g() {
    MutableLocation foo = MutableLocation(1, 2);
    f(foo);
    System.out.println("foo.x is " + foo.x); // Prints "foo.x is 1".
}

न ही आप ऑब्जेक्ट की कॉपी पास कर रहे हैं।

void f(MutableLocation foo) {  
    foo.x = 42;
}

void g() {
    MutableLocation foo = new MutableLocation(0, 0);
    f(foo);
    System.out.println("foo.x is " + foo.x); // Prints "foo.x is 42"
}

1 - पायथन और रूबी जैसी भाषाओं में, "शेयरिंग पास" शब्द को किसी वस्तु / संदर्भ के "पास बाय वैल्यू" के लिए पसंद किया जाता है।

2 - "संदर्भ द्वारा पास" या "कॉल बाय संदर्भ" शब्द का प्रोग्रामिंग भाषा शब्दावली में बहुत विशिष्ट अर्थ है। वास्तव में, इसका मतलब है कि आप एक चर या सरणी तत्व के पते को पास करते हैं, ताकि जब बुलाया विधि औपचारिक तर्क के लिए एक नया मान प्रदान करता है, तो यह मूल चर में मूल्य को बदल देता है। जावा इसका समर्थन नहीं करता है। गुजरने वाले मापदंडों के लिए अलग-अलग तंत्रों के अधिक विवरण के लिए, कृपया https://en.wikipedia.org/wiki/Evaluation_strategy देखें

नुकसान: असाइनमेंट और साइड-इफेक्ट्स का संयोजन

कभी-कभी हम StackOverflow Java प्रश्न (और C या C ++ प्रश्न) देखते हैं जो पूछते हैं कि ऐसा क्या है:

i += a[i++] + b[i--];

i ... a और b के कुछ ज्ञात प्रारंभिक अवस्थाओं के लिए ... का मूल्यांकन करता है।

आम तौर पर बोलना:

  • जावा के लिए उत्तर हमेशा 1 निर्दिष्ट होता है, लेकिन गैर-स्पष्ट, और अक्सर यह पता लगाने के लिए मुश्किल होता है
  • C और C ++ के लिए उत्तर अक्सर अनिर्दिष्ट होता है।

इस तरह के उदाहरण अक्सर परीक्षा या नौकरी के साक्षात्कार में देखने के प्रयास के रूप में उपयोग किए जाते हैं कि क्या छात्र या साक्षात्कारकर्ता यह समझता है कि जावा प्रोग्रामिंग भाषा में अभिव्यक्ति का मूल्यांकन वास्तव में कैसे काम करता है। यह "ज्ञान के परीक्षण" के रूप में यकीनन वैध है, लेकिन इसका मतलब यह नहीं है कि आपको कभी भी वास्तविक कार्यक्रम में ऐसा करना चाहिए।

वर्णन करने के लिए, निम्नलिखित प्रतीत होता है सरल उदाहरण स्टैकऑवरफ्लो सवालों (जैसे यह एक ) में कुछ बार दिखाई दिया है। कुछ मामलों में, यह किसी के कोड में एक वास्तविक गलती के रूप में प्रकट होता है।

int a = 1;
a = a++;
System.out.println(a);    // What does this print.

अधिकांश प्रोग्रामर (जावा विशेषज्ञों सहित) उन बयानों को जल्दी से पढ़ते हुए कहेंगे कि यह 2 आउटपुट करता है। वास्तव में, यह 1 आउटपुट करता है। क्यों की विस्तृत व्याख्या के लिए, कृपया इस उत्तर को पढ़ें।

हालाँकि, इस और इसी तरह के उदाहरणों से वास्तविक रूप से स्पष्ट है कि कोई भी जावा स्टेटमेंट जो एक ही वेरिएबल को बताता है और साइड-इफ़ेक्ट दोनों को समझने में सबसे कठिन है, और सबसे बुरी तरह से भ्रामक है। आपको इस तरह कोड लिखने से बचना चाहिए।


1 - मॉडुलो संभावित मुद्दे जावा मेमोरी मॉडल के साथ यदि चर या ऑब्जेक्ट अन्य थ्रेड्स के लिए दिखाई देते हैं।

नुकसान: यह नहीं समझना कि स्ट्रिंग एक अपरिवर्तनीय वर्ग है

नए जावा प्रोग्रामर अक्सर भूल जाते हैं, या पूरी तरह से समझने में विफल रहते हैं, कि जावा String क्लास अपरिवर्तनीय है। यह निम्नलिखित उदाहरण में एक जैसी समस्याओं की ओर जाता है:

public class Shout {
    public static void main(String[] args) {
        for (String s : args) {
            s.toUpperCase();
            System.out.print(s);
            System.out.print(" ");
        }
        System.out.println();
    }
}

उपर्युक्त कोड ऊपरी मामले में कमांड लाइन तर्क मुद्रित करने के लिए माना जाता है। दुर्भाग्य से, यह काम नहीं करता है, तर्कों का मामला नहीं बदला जाता है। समस्या यह है कि यह कथन है:

s.toUpperCase();

आप सोच सकते हैं कि कॉल toUpperCase() एक अपरकेस स्ट्रिंग के लिए s को बदलने जा रहा है। यह नहीं है यह नहीं कर सकता! String ऑब्जेक्ट अपरिवर्तनीय हैं। उन्हें बदला नहीं जा सकता।

हकीकत में, toUpperCase() विधि रिटर्न एक String वस्तु का एक अपरकेस संस्करण है जो String है कि आप पर यह कहते हैं। यह संभवतः एक नया String ऑब्जेक्ट होगा, लेकिन यदि s पहले से ही सभी अपरकेस था, तो परिणाम मौजूदा स्ट्रिंग हो सकता है।

तो इस विधि को प्रभावी ढंग से उपयोग करने के लिए, आपको विधि कॉल द्वारा लौटाए गए ऑब्जेक्ट का उपयोग करने की आवश्यकता है; उदाहरण के लिए:

s = s.toUpperCase();

वास्तव में, "स्ट्रिंग कभी नहीं बदलते" नियम सभी String विधियों पर लागू होता है। यदि आपको वह याद है, तो आप शुरुआती की गलतियों की एक पूरी श्रेणी से बच सकते हैं।



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