В чем причина этого окончательного предложения, содержащего вызовы close()
Я изучаю онлайн-курс java, Введение в программирование с использованием Java.
В главе о вводе-выводе приведен следующий код со следующим утверждением:
Кстати, в конце этой программы вы найдете наш первый полезный пример предложения finally в инструкции try. Когда компьютер выполняет оператор try, команды в его предложении finally гарантированно выполняются независимо от того, что.
Программа находится в конце раздела 11.2.1 и представляет собой простую программу, которая просто считывает некоторые числа из файла и записывает их в обратном порядке.
Соответствующий код в основном методе (данные - это Reader, а результат - Writer):
try {
// Read numbers from the input file, adding them to the ArrayList.
while ( data.eof() == false ) { // Read until end-of-file.
double inputNumber = data.getlnDouble();
numbers.add( inputNumber );
}
// Output the numbers in reverse order.
for (int i = numbers.size()-1; i >= 0; i--)
result.println(numbers.get(i));
System.out.println("Done!");
} catch (IOException e) {
// Some problem reading the data from the input file.
System.out.println("Input Error: " + e.getMessage());
} finally {
// Finish by closing the files, whatever else may have happened.
data.close();
result.close();
}
Итак, мне было интересно, почему предложение finally было полезно в этом случае, когда нет других точек выхода из предложений try или catch. Не могли ли тесные методы просто оказаться в главном корпусе?
Я подумал, что, возможно, это было потому, что теоретически может быть какое-то другое исключение RuntimeException, которое может привести к сбою программы, а затем оставить читателей и писателей незакрытыми, но тогда не будет ли факт, что программа все равно повредила их?
Ответы
Ответ 1
Ваша идея верна: блок finally закрывает ресурсы, даже если возникло непредвиденное исключение.
Вы также правы, что это не имеет значения, если такое исключение вызывает полное приложение, но, глядя на этот код, вы не можете решить, так ли это. Могут быть и другие обработчики исключений, вылавливая это исключение, поэтому хорошая и правильная практика заключается в том, чтобы поставить логику закрытия в блок finally.
Заметьте, что может быть скрыта ошибка: if data.close()
генерирует исключение, result.close()
никогда не будет вызван.
В зависимости от вашей среды существуют различные варианты устранения ошибки.
-
в java 7 ff вы можете использовать try-with-resources
-
Если вы используете Spring, может быть правильный шаблон, похожий на JdbcTemplate
-
если ничего из этого не будет применено, да, вам нужно будет попробовать/наконец в конце. Выходите некрасиво. Вы абсолютно должны хотя бы извлечь это в метод, как это предлагается в комментариях.
-
концептуально более чистый, но довольно многословный в java pre 8 - это реализовать шаблон кредита. Если вы не работаете с scala/clojure/haskell разработчиками, это может быть более запутанным, чем что-либо еще.
Ответ 2
Это по очень простой причине: это самый безопасный способ, pre Java 7 и try-with-resources, чтобы гарантировать, что ваши ресурсы будут закрыты, даже если будет обнаружено исключение.
Посмотрите, что произошло, если вы сделали это:
try {
// some code, then
resource.close();
} catch (SomeException e) {
// etc
}
Если ваш SomeException
вызывается перед закрытием ресурса, вы можете утечки ресурсов. С другой стороны, включение resource.close()
в finally
гарантирует, что он закрывается независимо от того, что еще происходит.
С Java 7 вы должны использовать это:
try (
final InputStream in = Files.newInputStream(Paths.get("somefile"));
// Others
) {
// work with "in" and others
} catch (Whatever e) {
}
Ваши ресурсы затем будут закрыты до catch
.
В качестве побочного примечания, используя Java 6, самый безопасный способ закрыть ваши ресурсы - использовать Guava Closer
.
Ответ 3
Если программа завершена после этого, тогда да, это также закроет ресурсы ввода-вывода.
Но многие программы не заканчиваются. Есть некоторые, которые должны работать 24/7 годами. Таким образом, очистка ваших ресурсов должным образом является обязательной.
К сожалению, Java < 7 предлагает только автоматический механизм очистки памяти (сбор мусора). С помощью Java 7 вы получаете новый " пример try-with-resource", который пытается подключить отверстие.
Если вы не можете использовать эту версию, вам нужно сделать очистку самостоятельно.
Тем не менее, код выше по-прежнему не работает: close()
может сам вызывать исключение, поэтому некоторые из ресурсов ввода-вывода могут все еще оставаться. Вместо этого вы должны использовать такие инструменты, как IOUtils.closeQuietly():
Reader reader = null;
try {
reader = ...open...
...use reader...
} finally {
IOUtils.closeQuietly(reader);
}
Ответ 4
Из документа Java:
Блок finally всегда выполняется, когда блок try завершается. Это гарантирует, что блок finally будет выполнен, даже если произойдет непредвиденное исключение. Но, наконец, полезно не только для обработки исключений - это позволяет программисту избежать случайного обхода кода очистки путем возврата, продолжения или разрыва. Ввод кода очистки в блок finally всегда является хорошей практикой, даже если не ожидается никаких исключений.
И о ваших проблемах:
тот факт, что программа все равно повредила их.
Ресурсы выделяются на уровне OS
, а не в вашей программе, поэтому, если ваша программа не имеет возможности очистить, то ресурсы будут выделены с использованием действительно.
Ответ 5
True. Если RuntimeException
происходит, то, наконец, будет выполнено выполнение, закрывая ресурсы, и это не теоретически. Это практический сценарий.
Кроме того, даже если IOException
(или многие другие, которых вы поймали), происходит. finally
запрещает вам писать один и тот же код для закрытия много раз.
Ответ 6
Это фактически правильная концепция, которую вы сформировали. Когда появляется CheckedException
как IOException
, тогда все ресурсы должны быть закрыты. Это происходит потому, что:
-
Когда вы выполняли программу, ресурсы были
доступ. Предположим, вы изменили несколько вещей, и вы не
сохранил их, вы не получите обновленный файл. (Это происходит, когда
вы используете Буферы - они не записывают данные мгновенно, они
пишите в куски).
-
Поскольку файл был открыт в JVM, есть вероятность, что он останется открытым, и вам нужно будет закрыть приложение youre
с помощью. Следовательно, вам нужно close()
ресурсы, чтобы
буферы сбрасываются, то есть сохраняются изменения.
Например:
try {
BufferedReader br = new BufferredReader (new FileReader("Example.txt"));
ArrayList<String> lines = new ArrayList<>();
String line;
while ( (line = br.readLine()) != null ) {
lines.add(line);
}
catch (IOException ie) {
//Error handling.
} finally {
br.close();
}
EDIT: с появлением JDK1.7 теперь вы можете использовать try with resources
, как показано ниже:
try (BufferedReader br = new BufferredReader (new FileReader("Example.txt"));) {
ArrayList<String> lines = new ArrayList<>();
String line;
while ( (line = br.readLine()) != null ) {
lines.add(line);
}
catch (IOException ie) {
//Error handling.
}
Теперь блок finally
не нужен, потому что BufferedReader
реализует AutoCloseable
. Как следует из названия, он автоматически закрывает буфер, когда остается блок try (..)
.
Ответ 7
Наконец, обеспечивается, что кусок кода всегда вызывается. Из-за этого лучше всего закрыть соединения. Я бы посоветовал также положить ваши закрытые утверждения в try catch, так как в блоке finally все еще может быть не так:
finally {
if (data != null) try { data.close() } catch(exception ex) { ex.printstacktrace() }
}
Ответ 8
В случае, когда исключение выбрано или не выбрано, предложение finally
гарантирует, что потоки data
и result
будут закрыты. В противном случае они могут не закрываться.
Ответ 9
Метод "закрыть" может сделать больше, чем просто сообщить операционной системе, что ресурс больше не требуется. Среди прочего, объекты, которые инкапсулируют различные формы потоков данных, могут самостоятельно накапливать определенный объем данных и передавать их в операционную систему либо при их закрытии, либо при накоплении определенного количества данных. Наличие объекта, который инкапсулирует поток данных журнала, передает данные в ОС в больших блоках, может быть намного более эффективным, чем перенос событий журнала в ОС по отдельности, но если программа умирает без "закрытия" файла журнала, информация, которая вероятно, будет очень актуальным для тех, кто диагностирует проблему, будет потерян.