9

3 つの質問があります。

BufferedReader説明するために、私は誰かのコードを見直していて、時々 s が閉じられていないことに気付きました。通常、Eclipse は、これがメモリ リークの可能性があるという警告を表示します (そして、私はそれを修正します)。ただし、 Callable 内部クラス内では、警告はありません。

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;   
        }
    }
}

コードを書いたのは経験豊富な Java 開発者なので、最初は意図的なものだと思いましたが、急いで書いて見落としていたのかもしれません。

私の質問は次のとおりです。

  1. Eclipse がこれを強調しないのはなぜですか (次の質問への回答によって回答される場合があります)

  2. call() メソッド内で閉じられた場合に起こりうる最悪の事態は何ですか? (正当な理由は思いつきません...そしてしばらく探していました...しかし、BufferedReaderを閉じないのは意図的だったのかもしれません)

  3. 内部クラス内で BufferedReader が閉じられていない場合に起こりうる最悪の事態は何ですか?

4

5 に答える 5

7

BufferedReader彼らは与えられた の周りにを作成しているのでInputStream、コードは を呼び出さなくても安全だと思いますclose()。呼び出すコードclose()は常に、ストリームを作成し、try/finally を使用して実行するコードである必要があります。

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);
}

これは機能します。writeLine メソッドは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それを実行してみてください。2回目の呼び出しで毎回失敗します。ストリームは、渡された場所ではなく、作成された場所で常に閉じることをお勧めします。最初は問題ないかもしれませんが、後でそのコードを変更しようとすると、エラーが発生する可能性があります。悪いメソッドを使用して1 回だけ呼び出しを開始しwriteLine、他の誰かが 2 回目の呼び出しを追加したい場合writeLine、ストリームを閉じないようにコードをリファクタリングする必要があります。幸せに近づくと、後で頭痛の種になる可能性があります。

また、技術的には、BufferedWriterはシステム リソースへの実際のハンドルではないことに注意してください。FileOutputStreamそのため、とにかく実際のリソースを閉じる必要があります。

したがって、経験則: ストリームを作成した場所でのみストリームを閉じ、作成と終了は常に try/finally ブロック (または Java 7 の素晴らしいtry/resource ブロックで終了します) で行います。

于 2012-08-30T18:50:38.503 に答える
2

この場合、 を閉じたくない場合がありBufferedReaderます。コンストラクタに渡されたInputStreamは、システム リソースに関連付けることができるオブジェクトです。BufferedReaderと は、そのInputStreamReader周りの単なるラッパーです。を閉じるBufferedReaderと も閉じますがInputStream、これは発信者が望んでいたものではない可能性があります。

于 2012-08-30T18:53:02.820 に答える
1

BuffereddReader close()

このストリームを閉じて、それに関連付けられているシステムリソースを解放します。ストリームがすでに閉じられている場合、このメソッドを呼び出しても効果はありません。

したがって、close()を実行しないと、システムリソースがリーダーに関連付けられたままになり、メモリリークが発生する可能性があります。

eclipseが強調表示されない理由:close()の呼び出しを無視してもコンパイル時エラーではないため、eclipseは強調表示されません。

于 2012-08-30T18:38:03.550 に答える
1
  1. ストリームは呼び出しメソッドの外のどこかで閉じることができるため、強調表示されません。これは真実です。

  2. 呼び出しメソッド内で閉じられている場合、他のスレッドが現在それを使用している可能性があります。

  3. BufferdRreaderストリームへの参照を失い、それを閉じることができず、これがメモリリークにつながる場合以外は何もありません。

于 2012-08-30T18:56:59.093 に答える
1

ストリームをどこで開いても、finally ブロックで閉じる必要があります。ストリームをテストすることもお勧めしnullます。ファイルが存在しない場合、ストリームはnull例外がスローされます ( FileNotFoundException) が、最終的にボックができました。つまり、call メソッドの内容は次のようになります。

   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;

または、Java 7 の使用が許可されている場合はAutoCloseable、この目的のためにインターフェイスと新しい言語構造の利点を利用できます。http://www.oracle.com/technetwork/articles/java/trywithresources-401775.htmlを参照してください。

于 2012-08-30T18:51:31.593 に答える