Что происходит с BufferedReader, который не закрывается внутри вызываемого объекта.позвонить?
У меня есть три вопроса.
Чтобы объяснить, я просматривал чей-то код и заметил, чтоBufferedReader
иногда не закрываются. Обычно Eclipse предупреждает, что это потенциальная утечка памяти (и я ее исправляю). Однако внутри вызываемого внутреннего класса нет никакого предупреждения.
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, поэтому моя первая мысль заключается в том, что это преднамеренно... но может быть, они торопились, когда писали его, и просто проглядели оно.
Мои вопросы:
-
Почему Eclipse не выделяет это (на что можно ответить ответом на следующие вопросы)?
-
Что самое худшее, что может произойти, если он закрыт в методе call ()? (Я не могу придумать хорошей причины... и я уже некоторое время занимаюсь поисками... но, возможно, это было намеренно, чтобы не закрыть BufferedReader)
-
Что самое худшее, что может произойти, если BufferedReader Нет закрыто внутри внутреннего класса?
5 ответов:
Я бы сказал, что, поскольку они создают
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
. Хорошая практика-всегда закрывать потоки там, где они создаются, а не там, где они передаются. Это может быть нормально изначально, но попытка изменить этот код позже может привести к ошибкам. Если бы я начал только с 1writeLine
вызова с плохим методом, а кто-то другой захотел бы добавить второй, им пришлось бы рефакторировать код, чтобыwriteLine
не закрывал поток в любом случае. Близость к счастью может вызвать головную боль в будущем.Также обратите внимание, что технически
BufferedWriter
не является фактическим дескриптором для вашего системного ресурса, аFileOutputStream
является, поэтому вы должны закрывать фактический ресурс в любом случае.Итак, правило thumb: закрывайте потоки только там, где вы их создаете, и всегда делайте создание и закрытие в блоке try/finally (или awesome try/resource block Java 7, который делает закрытие за вас).
BuffereddReader закрыть()
Закрывает этот поток и освобождает все системные ресурсы, связанные с оно. Если поток уже закрыт, то вызов этого метода не имеет никакого значения. эффект.
Таким образом, если вы не закроете (), системные ресурсы все еще могут быть связаны с устройством чтения, что может привести к утечке памяти.
Почему eclipse не выделяет: это не ошибка времени компиляции, если вы игнорируете вызов close (), поэтому eclipse не выделяет.
В этом случае вы можете не закрывать
BufferedReader
.InputStream
, который был передан конструктору, является объектом, который может быть связан с системным ресурсом.BufferedReader
иInputStreamReader
- это просто обертки вокруг этого. ЗакрытиеBufferedReader
также закроетInputStream
, что может быть не тем, что хотел вызывающий.
Не выделяет, и это правда, потому что поток может быть закрыт где-то еще из метода вызова.
Если он закрыт внутри метода вызова, то другие потоки могут использовать его в данный момент.
С
BufferdRreader
ничего, но если вы потеряете ссылку на поток и не можете закрыть его, и это приводит к утечке памяти.
Независимо от того, где вы открываете поток, вы должны закрыть его в блоке finally, и это хорошая практика, чтобы проверить поток для
null
, потому что если файл не существует, поток будетnull
, исключение будет брошено (FileNotFoundException
), но в конечном итоге bock делается. То есть содержание метода вызова должно быть: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