Наконец, иногда меня путают
Сегодня в колледже мы немного поговорили о try
, catch
и finally
.
Я запутался в этих двух примерах:
PrintWriter out = null;
try {
out = new PrintWriter(...); // We open file here
} catch (Exception e) {
e.printStackTrace();
} finally { // And we close it here
out.close();
}
В чем разница между закрытием файла в finally
и если мы просто сделали это следующим образом:
PrintWriter out = null;
try {
out = new PrintWriter(...); // We open file here
} catch (Exception e) {
e.printStackTrace();
}
out.close();
Этот фрагмент кода после catch всегда будет выполняться.
Можете ли вы дать мне несколько хороших примеров различий между тем, когда мы используем finally
, и когда мы помещаем код после catch? Я знаю, что, наконец, это всегда будет выполняться, но программа также будет работать после блока catch.
Ответы
Ответ 1
Это все равно будет иметь значение, если код вызывает Error
. Это не попадает в код, и поэтому любая часть после try/catch/finally
не будет обнаружена. Если это часть finally
, она будет выполняться даже для Error
.
Во-вторых, если по какой-либо причине e.printStackTrace()
выдает исключение (хотя это было бы очень редко), то же самое произойдет - finally
будет выполняться.
В целом finally
- очень безопасный способ освобождения ресурсов независимо от того, что происходит. Еще более безопасно try-with-resources, поддерживаемое с Java 7, поскольку оно может легко управлять несколькими исключениями, которые были созданы во время близких операций. В этом примере это будет выглядеть так:
try (PrintWriter out = new PrintWriter(...)) {
// do whatever with out
}
catch (Exception e) {
e.print... (whatever)
}
// no need to do anything else, close is invoked automatically by try block
EDIT: Также обратите внимание, что ваш код не совсем прав (независимо от того, какая версия). Если конструктор PrintWriter
генерирует исключение, строка out.close()
завершится с ошибкой NullPointerException
.
Ответ 2
Если в блоке try
возникает непрозрачная ошибка или даже если в вашем блоке catch
возникает ошибка, "фрагмент кода" после улова не будет выполнен, но блок finally
.
finally
всегда будет выполняться.
Из документации Java:
Блок finally всегда выполняется, когда блок try завершается. Эта гарантирует, что блок finally будет выполнен, даже если неожиданное исключение. Но, наконец, полезно не только для исключения обработка - это позволяет программисту избежать кода очистки случайно обходится возвратом, продолжается или прерывается. Помещение очистки код в блоке finally всегда является хорошей практикой, даже если нет исключения ожидаются.
Ответ 3
Что, если что-то в блоке catch будет выбрасывать Exception? out.close не будет выполняться. Вы также можете использовать попробовать с ресурсами", чтобы убедиться, что все ресурсы закрыты после его использования. Попробуйте этот пример:
public static void withFinnaly() {
try {
throwException();
System.out.println("This won't execute");
} catch (Exception e) {
System.out.println("Exception is caught");
throwException();
} finally {
System.out.println("Finally is always executed," +
" even if method in catch block throwed Exception");
}
}
public static void withOutFinnaly() {
try {
throwException();
System.out.println("This won't execute");
} catch (Exception e) {
System.out.println("Exception is caught");
throwException();
}
System.out.println("Looks like we've lost this... " +
"This wont execute");
}
public static void throwException() throws RuntimeException {
throw new RuntimeException();
}
Ответ 4
Обычная usecase, наконец, заключается в том, что вы не хотите поймать исключение в том же методе.
в этом случае вы используете попытку с блоком finally без улова. Таким образом, вы можете гарантировать, что ваши ресурсы будут закрыты без необходимости улавливать исключение в самом методе.
Ответ 5
В Java исходный код:
void foo()
{
try {
if (W())
return;
}
catch (FooException ex) {
if (X())
throw;
}
finally {
Y();
}
Z();
}
будет преобразован компилятором в:
void foo()
{
try {
if (W()) {
Y();
return;
}
}
catch (FooException ex) {
if (X()) {
Y();
throw;
}
}
catch {
Y();
throw;
}
Y();
Z();
}
Эффект состоит в том, чтобы вызвать дублирование кода в блоке finally
все места, где контроль может оставить метод. Любой блок try
который имеет finally
, но не обработчик catch-all, эквивалентен одному с обработчиком catch-all, который сразу же бросает (при котором процессор может затем вставить копию кода finally
перед обработчиком catch-all.
Ответ 6
Второй пример может вызвать нежелательный NullPointerException
, если в конструкторе PrintWriter
возникает исключение.
Другая возможность заключается в том, что out.close();
выдаст ошибку, которая не была поймана.
Если вы переместите код в блок finally
, он будет всегда выполняться - независимо от того, успешный или успешный блок try. Это особенно полезно, если ваш try
-block вызывает исключение, которое не было обнаружено. В вашем втором примере это приведет к тому, что out.close()
не будет выполняться, тогда как с блоком finally он будет выполнен, даже если блок try
выдает нечеткую ошибку.
Ответ 7
Хотя не полные ответы сами, эти два примера использования try-finally
(mis) могут быть полезными:
public class JavaApplication3
{
static int foo()
{
try
{
return 6;
}
finally
{
return 4;
}
}
public static void main(String[] args)
{
System.out.println("foo: " + foo());
}
}
public class JavaApplication3
{
static int foo()
{
try
{
throw new Exception();
}
finally
{
return 4;
}
}
public static void main(String[] args)
{
System.out.println("foo: " + foo());
}
}
Оба выхода программ 4.
Причину этого можно найти в Глава 14.20.2 JLS
Оператор try с блоком finally выполняется первым выполнением блока try. Тогда есть выбор:
• Если выполнение блока try завершается внезапно из-за выброса значения V, тогда есть выбор:
[...]
- Если тип времени выполнения V не является совместимым с уловимым класс исключения любого предложения catch в заявлении try, тогда, наконец, блок выполняется. Тогда есть выбор:
[...]
> Если блок finally окончательно завершится по причине S, то оператор try завершается внезапно по причине S (а выброс значения V отбрасывается и забыл).
• Если выполнение блока try завершается внезапно по любой другой причине R, тогда выполняется блок finally, а затем есть выбор:
- Если окончательный блок завершается нормально, то инструкция try завершается внезапно по причине R.
- Если блок finally окончательно завершится по причине S, то оператор try завершается внезапно по причине S (и причина R отбрасывается).
Редактирование шахты
Считаем, что a return
является неожиданным способом завершения блока try
или finally
.
Ответ 8
Меньший пример:
PrintWriter out = null;
try {
out = new PrintWriter();
out.print(data);
} finally {
out.close();
}
Здесь мы не поймаем никаких исключений (обработанных вызывающим), но мы хотим, чтобы close
писатель ли мы
- работают нормально через блок
try
или
- оставить исключение.
В обоих случаях код в finally
выполняется как часть выхода из блока.
Здесь мы обнаруживаем подмножество исключений:
PrintWriter out = null;
try {
out = new PrintWriter();
out.print(data);
} catch (IOException e) {
log(e);
} finally {
out.close();
}
do_something_else();
Здесь есть три возможных пути:
- нормальное выполнение части
try
, за которой следует finally
, а затем do_something_else()
,
- фрагмент
try
throws IOException
, который пойман и зарегистрирован, затем выполняется блок finally
, а затем do_something_else()
или
- часть
try
выдает другой забрасываемый, который не пойман, но выполняется блок finally
, затем код переходит к следующему заключению try
, возможно, в вызывающем.
Соблюдайте осторожность при написании блока finally
- он не находится внутри try
, поэтому любые исключения там будут предотвращать любое исключение, которое выполняется. Короткий совет - попытаться избежать вещей, которые могут быть выбраны изнутри finally
или catch
.