जावा में अनंत लूप


82

whileजावा में निम्नलिखित अनंत लूप को देखें। यह नीचे दिए गए बयान के लिए एक संकलन-समय त्रुटि का कारण बनता है।

while(true) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //Unreachable statement - compiler-error.

निम्नलिखित एक ही अनंत whileलूप, हालांकि ठीक काम करता है और कोई त्रुटि जारी नहीं करता है जिसमें मैंने बस बूलियन चर के साथ स्थिति को बदल दिया है।

boolean b=true;

while(b) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.

दूसरे मामले में भी, लूप के बाद बयान स्पष्ट रूप से पहुंच से बाहर है क्योंकि बूलियन चर bअभी भी सच है कंपाइलर बिल्कुल भी शिकायत नहीं करता है। क्यों?


संपादित करें: निम्न संस्करण whileएक अनंत लूप में फंस जाता है जैसा कि स्पष्ट है, लेकिन नीचे दिए गए बयान के लिए कोई संकलक त्रुटियां जारी नहीं करता है, भले ही ifलूप के भीतर की स्थिति हमेशा होती है falseऔर परिणामस्वरूप, लूप कभी वापस नहीं आ सकता है और संकलक द्वारा निर्धारित किया जा सकता है संकलन-समय ही।

while(true) {

    if(false) {
        break;
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.

while(true) {

    if(false)  { //if true then also
        return;  //Replacing return with break fixes the following error.
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.

while(true) {

    if(true) {
        System.out.println("inside if");
        return;
    }

    System.out.println("inside while"); //No error here.
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.

संपादित करें: के साथ एक ही बात ifऔर while

if(false) {
    System.out.println("inside if"); //No error here.
}

while(false) {
    System.out.println("inside while");
    // Compiler's complain - unreachable statement.
}

while(true) {

    if(true) {
        System.out.println("inside if");
        break;
    }

    System.out.println("inside while"); //No error here.
}      

निम्न संस्करण whileभी एक अनंत लूप में फंस जाता है।

while(true) {

    try {
        System.out.println("inside while");
        return;   //Replacing return with break makes no difference here.
    } finally {
        continue;
    }
}

ऐसा इसलिए है क्योंकि finallyब्लॉक को हमेशा निष्पादित किया जाता है, भले ही यह returnबयान tryब्लॉक के भीतर ही सामने आए ।


46
किसे पड़ी है? यह स्पष्ट रूप से संकलक की एक विशेषता है। इस तरह के सामान के साथ अपने आप को चिंता मत करो।
CJ7

17
एक और धागा स्थानीय गैर-स्थिर चर को कैसे बदल सकता है?
CJ7

4
प्रतिबिंब के माध्यम से वस्तु की आंतरिक स्थिति को समवर्ती रूप से बदला जा सकता है। यही कारण है कि जेएलएस अनिवार्य है कि केवल अंतिम (स्थिर) अभिव्यक्तियों की जांच की जाए।
lsoliveira

5
मैं इन बेवकूफ त्रुटियों से नफरत करता हूं। अगम्य कोड एक चेतावनी होना चाहिए त्रुटि नहीं।
user606723

2
@ CJ7: मैं इसे एक "सुविधा" नहीं कहूंगा, और यह एक अनुरूप जावा कंपाइलर को लागू करने के लिए इसे बहुत थकाऊ (बिना किसी कारण के) बनाता है। अपने उप-डिज़ाइन वेंडर लॉक-इन का आनंद लें।
L --o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e

जवाबों:


105

संकलक आसानी से और असमान रूप से साबित कर सकता है कि पहली अभिव्यक्ति हमेशा एक अनंत लूप में होती है, लेकिन यह दूसरे के लिए उतना आसान नहीं है। आपके खिलौने के उदाहरण में यह सरल है, लेकिन क्या अगर:

  • चर की सामग्री एक फ़ाइल से पढ़ी गई थी?
  • चर स्थानीय नहीं था और किसी अन्य धागे द्वारा संशोधित किया जा सकता है?
  • चर कुछ उपयोगकर्ता इनपुट पर निर्भर करता है?

कंपाइलर स्पष्ट रूप से आपके सरल मामले की जांच नहीं कर रहा है क्योंकि यह पूरी तरह से उस सड़क पर जा रहा है। क्यों? क्योंकि यह कल्पना से बहुत कठिन है । देखें खंड 14.21 :

(वैसे, मेरा कंपाइलर वेरिएबल घोषित होने पर शिकायत करता है final।)


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

10
@StephenC - उस जानकारी के लिए धन्यवाद। खुशी से अद्यतन के रूप में ज्यादा प्रतिबिंबित करने के लिए।
वेन

फिर भी -1; आपका कोई भी "क्या अगर" यहां लागू नहीं होता है। जैसा कि यह है, संकलक वास्तव में समस्या को हल कर सकता है - स्थैतिक विश्लेषण के संदर्भ में इसे निरंतर-प्रसार कहा जाता है, और कई अन्य स्थितियों में सफलतापूर्वक उपयोग किया जाता है। जेएलएस ही एकमात्र कारण है, और समस्या को हल करना कितना कठिन है। बेशक जेएलएस लिखा है कि जिस तरह से पहली जगह में उस समस्या की कठिनाई से संबंधित हो सकता है, हालांकि मुझे व्यक्तिगत रूप से संदेह है कि यह वास्तव में है क्योंकि यह अलग-अलग संकलक में मानकीकृत निरंतर-प्रसार समाधान को लागू करना मुश्किल है ।
ओक

2
@ ओक - मैंने अपने उत्तर में माना कि " स्टीफन की टिप्पणी के आधार पर " ओपी का खिलौना उदाहरण सरल है " और, जेएलएस सीमित कारक है। मुझे पूरा यकीन है कि हम सहमत हैं।
वेन

55

विनिर्देशों के अनुसार , बयान करते समय निम्नलिखित के बारे में कहा जाता है।

एक बयान में सामान्य रूप से पूरा हो सकता है यदि निम्न में से कम से कम एक सच है:

  • जबकि कथन उपलब्ध है और मूल्य अभिव्यक्ति सत्य के साथ स्थिर अभिव्यक्ति नहीं है।
  • एक पहुंच योग्य ब्रेक स्टेटमेंट है जो कि स्टेटमेंट से बाहर निकलता है। "

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


3
+1 यह ध्यान देने के लिए कि यह केवल वही नहीं है जो संकलक लेखक सोच सकते हैं या नहीं कर सकते हैं - जेएलएस उन्हें बताता है कि वे क्या कर सकते हैं और पहुंच योग्य नहीं हो सकते।
यशवित

14

क्योंकि सत्य निरंतर है और b को लूप में बदला जा सकता है।


1
सही है, लेकिन अप्रासंगिक है, क्योंकि ख पाश में नहीं बदला जा रहा है। आप समान रूप से तर्क दे सकते हैं कि जो लूप सच का उपयोग करता है उसमें ब्रेक स्टेटमेंट शामिल हो सकता है
डेवॉर्ड डे

@deworde - जब तक एक मौका है कि इसे बदला जा सकता है (एक और सूत्र द्वारा, कहते हैं) तब कंपाइलर इसे त्रुटि नहीं कह सकता। आप केवल लूप को देखकर नहीं बता सकते हैं कि लूप चालू होने के दौरान बी को बदला जा सकता है या नहीं।
पीटर रेकोर

10

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

कंपाइलर को ट्रिक करने के कई तरीके हैं - एक और आम उदाहरण है

public void test()
{
    return;
    System.out.println("Hello");
}

जो काम नहीं करेगा, क्योंकि कंपाइलर को एहसास होगा कि यह क्षेत्र अप्राप्य है। इसके बजाय, आप कर सकते हैं

public void test()
{
    if (2 > 1) return;
    System.out.println("Hello");
}

यह काम करेगा क्योंकि संकलक यह महसूस करने में असमर्थ है कि अभिव्यक्ति कभी झूठी नहीं होगी।


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

मैं JLS तर्क का पालन नहीं करता। क्या कंपाइलर्स वास्तव में सभी कानूनी जावा को बाइट कोड में बदलने के लिए बाध्य हैं? भले ही वह निरर्थक सिद्ध हो।
एमोरी

@emory हाँ, यह एक मानक-अनुरूप ब्राउज़र की परिभाषा होगी, कि यह मानक का पालन करता है और कानूनी जावा कोड का संकलन करता है। और यदि आप एक गैर-मानक-अनुरूप ब्राउज़र का उपयोग करते हैं, जबकि इसमें कुछ अच्छी विशेषताएं हो सकती हैं, तो आप अब एक व्यस्त संकलक डिजाइनर के सिद्धांत पर भरोसा कर रहे हैं कि "समझदार" नियम क्या है। जो वास्तव में भयानक परिदृश्य है: "कोई भी दो ही स्थितियों में TWO सशर्त का उपयोग क्यों करना चाहेगा ? यदि मेरे पास कभी नहीं है")
डेवॉर्ड

यह उपयोग करने ifवाला उदाहरण ए से थोड़ा अलग है while, क्योंकि कंपाइलर if(true) return; System.out.println("Hello");शिकायत किए बिना भी अनुक्रम संकलित करेगा । IIRC, यह JLS में एक विशेष अपवाद है। कंपाइलर के ifरूप में आसान के बाद अगम्य कोड का पता लगाने में सक्षम हो जाएगा while
क्रिश्चियन सेमरू

2
@emory - जब आप किसी जावा प्रोग्राम को तृतीय-पक्ष एनोटेशन प्रोसेसर के माध्यम से खिलाते हैं, तो यह ... बहुत वास्तविक अर्थों में ... जावा नहीं है। इसके बजाय, यह जो कुछ भी भाषाई विस्तार और संशोधन प्रोसेसर एनोटेशन लागू करता है के साथ जावा अतिव्यापी है । लेकिन यह नहीं है कि यहाँ क्या हो रहा है। ओपी के सवाल वेनिला जावा के बारे में हैं, और जेएलएस नियम यह बताता है कि एक वैध वैनिला जावा प्रोग्राम क्या है और क्या नहीं है।
स्टीफन C

6

उत्तरार्द्ध अगम्य नहीं है। बूलियन बी में अभी भी लूप के अंदर कहीं झूठे होने की संभावना है, जिससे एक समाप्त होने की स्थिति पैदा होती है।


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

4

मेरा अनुमान है कि वैरिएबल "बी" में इसके मूल्य को बदलने की संभावना है, इसलिए कंपाइलर थिंक System.out.println("while terminated"); तक पहुंचा जा सकता है।


4

कंपाइलर सही नहीं हैं - और न ही वे होने चाहिए

संकलक की जिम्मेदारी सिंटैक्स की पुष्टि करना है - निष्पादन की पुष्टि नहीं करना। कंपाइलर अंततः एक जोरदार टाइप की गई भाषा में कई रन टाइम समस्याओं को पकड़ सकते हैं और रोक सकते हैं - लेकिन वे ऐसी सभी त्रुटियों को नहीं पकड़ सकते हैं।

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

मजबूत टंकण और ऊ: बढ़ती संकलक की प्रभावकारिता

कुछ त्रुटियां प्रकृति में वाक्यात्मक हैं - और जावा में, मजबूत टाइपिंग बहुत सारे रन टाइम को अपवाद बनाती है। लेकिन, बेहतर प्रकारों का उपयोग करके, आप बेहतर तर्क लागू करने के लिए अपने संकलक की मदद कर सकते हैं।

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

इसका एक क्लासिक उदाहरण इट्रेटर पैटर्न का उपयोग है, जावा के फॉर्चेस लूप के साथ मिलकर यह निर्माण उस प्रकार के बग के लिए कम संवेदनशील है जिसे आप लूप करते समय एक सरलीकृत की तुलना में वर्णन करते हैं।


ध्यान दें कि OO केवल स्थैतिक टाइपिंग तंत्र को त्रुटियों को खोजने में मदद करने का एकमात्र तरीका नहीं है। हास्केल के पैरामीट्रिक प्रकार और प्रकार की कक्षाएं (जो कि ओओ भाषाओं में कक्षाएं कहा जाता है से थोड़ी अलग हैं) वास्तव में इस पर काफी बेहतर हैं।
बायीं ओर सबटैबआउट

3

संकलक उन मानों के माध्यम से चलने के लिए पर्याप्त परिष्कृत नहीं है, bजिनमें शामिल हो सकते हैं (हालांकि आप इसे केवल एक बार असाइन करते हैं)। पहला उदाहरण संकलक के लिए आसान है यह देखने के लिए कि यह एक अनंत लूप होगा क्योंकि स्थिति परिवर्तनशील नहीं है।


4
यह संकलक के "परिष्कार" से संबंधित नहीं है। जेएलएस अगम्य कथनों का पता लगाने के लिए नियमों को निर्दिष्ट करने के लिए बड़ी लंबाई तक जाता है। उन नियमों के अनुसार, पहला उदाहरण वैध जावा नहीं है, और दूसरा उदाहरण वैध जावा है। बस। संकलक लेखक को निर्दिष्ट नियमों को लागू करना चाहिए ... अन्यथा उसका संकलक गैर-अनुरूप है।
स्टीफन सी

3

मुझे आश्चर्य है कि आपके कंपाइलर ने पहले मामले को संकलित करने से इनकार कर दिया। यह मुझे अजीब लगता है।

लेकिन दूसरा मामला पहले मामले के लिए अनुकूलित नहीं है क्योंकि (ए) एक और धागा b(बी) के मूल्य को अद्यतन कर सकता है जिसे बुलाया फ़ंक्शन bसाइड इफेक्ट के रूप में बदल सकता है।


2
यदि आप आश्चर्यचकित हैं, तो आपने जावा भाषा स्पेस :-)
स्टीफन सी

1
Haha :) यह जानना अच्छा है कि मैं कहां खड़ा हूं। धन्यवाद!
सितंबर

3

वास्तव में मुझे नहीं लगता कि किसी को भी यह सही मिला (कम से कम मूल प्रश्नकर्ता के अर्थ में नहीं)। OQ का उल्लेख रहता है:

सही है, लेकिन अप्रासंगिक है, क्योंकि ख पाश में नहीं बदला जा रहा है

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

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

मैंने किसी अन्य वर्ग के गैर-अंतिम निजी चर को एक खरीदे गए पुस्तकालय में बंदर-पैच को संशोधित करने के लिए प्रतिबिंब की क्षमता का उपयोग किया है - एक बग को ठीक करना ताकि हम विक्रेता से आधिकारिक पैच की प्रतीक्षा करते समय हम विकसित करना जारी रख सकें।

वैसे, यह वास्तव में इन दिनों काम नहीं कर सकता है - हालांकि मैंने इसे पहले किया है, एक मौका है कि सीपीयू कैश में इस तरह के एक छोटे लूप को कैश किया जाएगा और चूंकि चर को अस्थिर नहीं किया गया है, कैश्ड कोड कभी नहीं हो सकता है नया मूल्य उठाओ। मैंने इसे कभी कार्रवाई में नहीं देखा है, लेकिन मेरा मानना ​​है कि यह सैद्धांतिक रूप से सच है।


अच्छी बात। मुझे लगता है कि bएक विधि चर है। क्या आप वास्तव में परावर्तन का उपयोग करते हुए एक विधि चर को संशोधित कर सकते हैं? भले ही समग्र बिंदु यह है कि हमें यह नहीं मान लेना चाहिए कि अंतिम पंक्ति उपलब्ध नहीं है।
एमोरी

आउच, आप सही हैं, मैंने मान लिया कि बी एक सदस्य था। यदि बी एक विधि चर है, तो मुझे विश्वास है कि संकलक "कैन" यह जान सकता है कि यह नहीं बदलेगा, लेकिन ऐसा नहीं करता है (जो अन्य सभी उत्तरों को बिल्कुल सही बनाता है)
बिल K

3

यह केवल इसलिए है क्योंकि संकलक बहुत अधिक बच्चे के बैठने का काम नहीं करता है, हालांकि यह संभव है।

दिखाया गया उदाहरण अनंत लूप का पता लगाने के लिए संकलक के लिए सरल और उचित है। लेकिन हम चर के साथ किसी भी संबंध के बिना कोड की 1000 लाइनें कैसे डालें b? और उन बयानों के बारे में कैसे b = true;? संकलक निश्चित रूप से परिणाम का मूल्यांकन कर सकता है और आपको बता सकता है कि यह वास्तव में whileलूप में सही है , लेकिन वास्तविक परियोजना को संकलित करने के लिए कितना धीमा होगा?

पुनश्च, लिंट उपकरण निश्चित रूप से यह आपके लिए करना चाहिए।


2

संकलक के दृष्टिकोण से bमें while(b)झूठी कहीं बदल सकता है। संकलक सिर्फ जाँच को परेशान नहीं करता है।

मजेदार कोशिश के लिए while(1 < 2), for(int i = 0; i < 1; i--)आदि।


2

अभिव्यक्तियों का मूल्यांकन रन टाइम पर किया जाता है, जब स्केलर वैल्यू को "सच" के स्थान पर बूलियन वैरिएबल की तरह बदल दिया जाता है, तो आपने स्केलर मान को बूलियन एक्सप्रेशन में बदल दिया और इस प्रकार, कंपाइलर के पास संकलन समय पर इसे जानने का कोई तरीका नहीं है।


2

यदि कंपाइलर निर्णायक रूप से यह निर्धारित कर सकता है कि बूलियन trueरन-टाइम पर मूल्यांकन करेगा , तो यह उस त्रुटि को फेंक देगा। संकलक मानता है कि आपके द्वारा घोषित चर को बदला जा सकता है (यद्यपि हम यहां जानते हैं कि मनुष्य के रूप में यह नहीं होगा)।

इस तथ्य पर जोर देने के लिए, यदि चर को finalजावा में घोषित किया जाता है, तो अधिकांश संकलक उसी त्रुटि को फेंक देंगे जैसे आपने मूल्य के लिए प्रतिस्थापित किया था। ऐसा इसलिए है क्योंकि चर को संकलित समय पर परिभाषित किया गया है (और रन-टाइम में नहीं बदला जा सकता है) और संकलक इसलिए निर्णायक रूप से यह निर्धारित कर सकता है कि अभिव्यक्ति trueरन-टाइम पर मूल्यांकन करती है ।


2

पहला स्टेटमेंट हमेशा एक अनंत लूप में परिणत होता है क्योंकि हमने लूप की स्थिति में एक निरंतरता को निर्दिष्ट किया है, जहां दूसरे मामले में कंपाइलर मानते हैं कि, लूप के अंदर बी के मूल्य में बदलाव की संभावना है।

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