2012-08-30 41 views
8

मेरे पास तीन प्रश्न हैं।एक BufferedReader का क्या होता है जो कॉल करने योग्य.call के भीतर बंद नहीं होता है?

समझाओ, मैं किसी के कोड की समीक्षा कर रहा था, और देखा कि BufferedReader कभी-कभी बंद नहीं किया जा रहा है। आमतौर पर, ग्रहण एक चेतावनी देता है कि यह एक संभावित स्मृति रिसाव है (और मैं इसे ठीक करता हूं)। हालांकि, एक कॉल करने योग्य आंतरिक कक्षा के भीतर, कोई चेतावनी नहीं है।

class outerClass { 
    ... 
    public void someMethod() { 
     Future<Integer> future = outputThreadPool.submit(new innerClass(this.myProcess.getInputStream(), threadName)); 
     ... 
    } 

    class innerClass implements Callable<Integer> { 
     private final InputStream stream; 
     private final String prepend; 

     innerClass(InputStream stream, String prepend) { 
      this.stream = stream; 
      this.prepend = prepend; 
     } 

     @Override 
     public Integer call() { 
      BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream)); 
      String output = null; 
      try { 
       while ((output = stdOut.readLine()) != null) { 
        log.info("[" + prepend + "] " + output); 
       } 

      } catch (IOException ignore) { 
      // I have no idea why we're ignoring this... :-|   
      } 
      return 0; 
     } 
    } 
} 

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

मेरे प्रश्न हैं:

  1. ग्रहण क्यों उजागर नहीं करता है यह (जो निम्न प्रश्नों के उत्तर द्वारा उत्तर दिया जा सकता है)?

  2. कॉल() विधि के भीतर बंद होने पर सबसे बुरा क्या हो सकता है? (मैं एक अच्छे कारण के बारे में नहीं सोच सकता ... और मैं थोड़ी देर के लिए खोज कर रहा हूं ... लेकिन हो सकता है कि यह जानबूझकर BufferedReader को बंद न करें)

  3. क्या हो सकता है कि सबसे बुरा क्या हो सकता है BufferedReader नहीं आंतरिक कक्षा के भीतर बंद हो गया है?

+1

नहीं चेतावनी पर चिह्नित करने और अधिक एक सचेत कहना –

+0

मैं एक जोड़े को जवाब है कि एक दूसरे के खंडन करने के लिए लग रहे हो "यह इस मामले में यह करने के लिए ठीक है" निर्णय से एक ग्रहण बग की तरह लगता है। एक ओर कुछ लोगों का कहना है कि BufferedReader बंद होना चाहिए। दूसरी तरफ, वे कहते हैं कि इसे बंद नहीं किया जाना चाहिए क्योंकि इसे बंद करना इनपुटस्ट्रीम को बंद कर देगा, और कॉलिंग क्लास को इसकी आवश्यकता हो सकती है ... – GLaDOS

+1

देखें [यह सवाल] (http://stackoverflow.com/questions/ 1388602/कर-ए-आवश्यकता के करीब दोनों-FileReader और bufferedreader)। यदि किसी इनपुट में 'इनपुटस्ट्रीम' जैसे संसाधन को पास किया जा रहा है, तो इसका मतलब है कि किसी और ने इसे खोला है, इसलिए किसी और को इसे बंद करना चाहिए। यदि वे आपकी विधि में एक स्ट्रीम पास करते हैं और फिर इसे कहीं और उपयोग करने का प्रयास करते हैं, तो यह टूट जाएगा क्योंकि यह बंद हो जाएगा ('बफर रीडर' को बंद करने से इसकी स्ट्रीम बंद हो जाती है), भले ही आपकी विधि को केवल एक चीज़ करने की आवश्यकता हो धारा। – Brian

उत्तर

6

मैं कहूंगा कि चूंकि वे को InputStream के आस-पास बना रहे हैं, तो कोड close() पर कॉल नहीं कर रहा है। कोड जो close() पर कॉल करता है हमेशा वह कोड होना चाहिए जो स्ट्रीम बनाता है और कोशिश/अंत का उपयोग करके किया जाता है।

public static void read(String str) throws IOException { 
    FileInputStream stream = null 
    try { 
     stream = new FileInputStream(str); 
     readStreamToConsole(stream); 
    } finally { 
     if (stream != null) 
      stream.close(); 
    } 
} 

private static void readStreamToConsole(InputStream stream) { 
    BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream)); 
    String output = null; 
    while ((output = stdOut.readLine()) != null) 
     System.out.println(output); 
} 

एक और नोट: आपका कोड किसी अन्य प्रक्रिया से आउटपुट लॉगिंग प्रतीत होता है। आप शायद स्ट्रीम को बंद करने में सक्षम नहीं होंगे। इसे स्वयं परीक्षण किए बिना, मुझे यकीन नहीं है कि अगर आप किसी अन्य प्रक्रिया से स्ट्रीम बंद कर देते हैं तो क्या होगा।

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


संपादित करने के लिए:

के एक उदाहरण के रूप में इस समय एक आउटपुट स्ट्रीम और BufferedWriter उपयोग करते हैं:

private static final String NEWLINE = System.getProperty("line.separator"); 

public static void main(String[] args) throws IOException { 
    String file = "foo/bar.txt"; 
    FileOutputStream stream = null; 
    try { 
     stream = new FileOutputStream(file); 
     writeLine(stream, "Line 1"); 
     writeLine(stream, "Line 2"); 
    } finally { 
     if (stream != null) 
      stream.close(); 
    } 
} 

private static void writeLine(OutputStream stream, String line) throws IOException { 
    BufferedWriter writer = new BufferedWriter(new InputStreamWriter(stream)); 
    writer.write(line + NEWLINE); 
} 

यह काम करता है। writer बनाने और वास्तव में फ़ाइल में एकल line लिखने के लिए लेखन विधि का उपयोग प्रतिनिधि के रूप में किया जाता है। बेशक, यह तर्क कुछ और जटिल हो सकता है, जैसे ऑब्जेक्ट को String में बदलना और इसे लिखना। यह main विधि को पढ़ने के लिए थोड़ा आसान बनाता है।

अब, अगर इसके बजाय, हमने BufferedWriter को बंद कर दिया?

private static void writeLine(OutputStream stream, String line) throws IOException { 
    BufferedWriter writer = null; 
    try { 
     writer = new BufferedWriter(new InputStreamWriter(stream)); 
     writer.write(line + NEWLINE); 
    } finally { 
     if (writer != null) 
      writer.close(); 
    } 
} 

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

यह भी ध्यान रखें कि तकनीकी रूप से, BufferedWriter आपके सिस्टम संसाधन के लिए वास्तविक संभाल नहीं है, FileOutputStream है, इसलिए आपको वास्तविक संसाधन पर वैसे भी बंद होना चाहिए।

तो, अंगूठे का नियम: केवल अपनी स्ट्रीम को बंद करें जहां आप उन्हें बनाते हैं, और हमेशा प्रयास/अंत में ब्लॉक (या जावा 7 के भयानक try/resource block) में सृजन और समापन करते हैं, जो आपके लिए बंद हो जाता है)।

+0

ठीक है आप ... मैं उस स्थान को देखने के लिए गया जहां स्ट्रीम वास्तव में बनाई गई थी ... कोड में बहुत आगे की ओर; और यह वहां बंद हो रहा है। धन्यवाद! – GLaDOS

+0

अन्य प्रक्रिया से आउटपुट लॉगिंग के बारे में भी सही है। यह प्रक्रिया प्रश्न में स्ट्रीम द्वारा स्ट्रीम की जा रही फ़ाइल पर कुछ काम कर रही थी, और लॉग में किसी भी संभावित त्रुटियों या समस्याओं को ट्रैक करना महत्वपूर्ण था। – GLaDOS

+0

अधिक विस्तृत उत्तर के लिए भी बहुत बढ़िया और धन्यवाद। आप मेरे देव प्रबंधक से अधिक मददगार थे जो लेखन के समय प्रश्न में कोड की देखरेख कर रहे थे :) – GLaDOS

1

BuffereddReader close()

इस धारा और विज्ञप्ति किसी भी प्रणाली इसके साथ जुड़े संसाधनों बंद करता है। अगर स्ट्रीम पहले से बंद है तो इस विधि का आह्वान करने के लिए प्रभाव नहीं है।

तो, यदि आप बंद नहीं करते हैं, तो, सिस्टम संसाधन अभी भी पाठक से जुड़े हो सकते हैं जो स्मृति रिसाव का कारण बन सकता है।

क्यों ग्रहण हाइलाइट नहीं कर रहा है: अगर आप बंद() को अनदेखा करते हैं तो यह समय त्रुटि संकलित नहीं है, इसलिए ग्रहण को हाइलाइट न करें।

+0

ठीक है कि आंशिक रूप से प्रश्न 3 का उत्तर देता है ... जो मैं समझता हूं। लेकिन, अगर यह धागे के अंदर है तो शायद जब थ्रेड निकल जाए तो यह कचरा इकट्ठा होगा? – GLaDOS

+0

हां, केवल BufferedReader ऑब्जेक्ट जीसीड हो सकता है, लेकिन अन्य सॉकेट से संबंधित सामान, मुझे नहीं लगता कि जीसी इसके लिए कुछ भी करता है। – kosa

+0

लेकिन ग्रहण _does_ इसे हाइलाइट करता है जब यह किसी भीतरी कक्षा में नहीं होता है ... यही वह है जो मुझे परेशान कर रहा है। – GLaDOS

1

इससे कोई फर्क नहीं पड़ता कि आप स्ट्रीम को खोलते हैं, आपको अंत में ब्लॉक में बंद करना चाहिए और null के लिए स्ट्रीम का परीक्षण करना एक अच्छा अभ्यास है, क्योंकि यदि फ़ाइल मौजूद नहीं है तो स्ट्रीम null होगी, अपवाद होगा फेंक दिया गया (FileNotFoundException) लेकिन अंततः बॉक किया जाता है। अर्थात। कॉल विधि की सामग्री होना चाहिए:

BufferedReader stdOut = null; 
    String output = null; 
     try { 
      stdOut = new BufferedReader(new InputStreamReader(stream)); 
      while ((output = stdOut.readLine()) != null) { 
       log.info("[" + prepend + "] " + output); 
      } 
     } catch (FileNotFoundException ex) { 
      log.warn("Unable to open nonexisten file " + whichOne); 
     } catch (IOException ex) { 
      log.warn("Unable to read from stream"); 
     } finally { 
      if (stdOut != null) { 
       try { 
        stdOut.close(); 
       } catch (IOException e) { 
        log.warn("Unable to close the stream"); 
       } 
      } 
     } 
     return 0; 

या आप जावा 7 उपयोग करने के लिए आप AutoCloseable इंटरफेस और इस उद्देश्य के लिए नई भाषा संरचना के लाभों का उपयोग कर सकते अनुमति दी जाती है। देखें http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

+0

stdOut कभी भी शून्य के बराबर नहीं हो सकता है, इसलिए बंद होने से पहले ब्लॉक में कोई बिंदु नहीं है। अगर निर्माण विफल हुआ, तो यह अंततः ब्लॉक तक नहीं पहुंच पाएगा। –

+0

ओह, आप सही हैं। मैंने इसे थोड़ा संपादित किया है। अब, यह सही है। –

+0

बहुत यकीन है कि हम जल्द ही 7 पर नहीं जायेंगे :( – GLaDOS

1

आप इस मामले में BufferedReader को बंद नहीं करना चाहते हैं। InputStream जो कि कन्स्ट्रक्टर को पास किया गया था वह ऑब्जेक्ट है जो सिस्टम संसाधन से जुड़ा हो सकता है। BufferedReader और InputStreamReader बस इसके चारों ओर रैपर हैं। BufferedReader को बंद करने से InputStream भी बंद हो जाएगा, जो कॉलर चाहता था कि हो सकता है।

1
  1. हाइलाइट नहीं करता है और यह सच है, क्योंकि किसी कॉल को कॉल विधि से कहीं और बंद किया जा सकता है।

  2. यदि यह कॉल विधि के भीतर बंद है, तो इस समय अन्य धागे इसका उपयोग कर रहे हैं।

  3. BufferdRreader के साथ कुछ भी नहीं, लेकिन यदि आप स्ट्रीम के संदर्भ को खो देते हैं और इसे बंद नहीं कर सकते हैं और इससे स्मृति रिसाव हो जाता है।

+0

संक्षिप्त के लिए +1 – GLaDOS