Вам действительно нужен блок «наконец»? - PullRequest
64 голосов
/ 05 октября 2010

В java есть 3 перестановки блока try ... catch ... finally.

  1. try ... catch
  2. try ... catch ...finally
  3. try ... finally

После выполнения блока finally управление переходит к следующей строке после блока finally.Если я уберу блок finally и переместу все его операторы в строку после блока try ... catch, это будет иметь тот же эффект, что и их размещение в блоке finally?

Ответы [ 8 ]

57 голосов
/ 08 мая 2014

Я знаю, что это очень старый вопрос, но я столкнулся сегодня, и я был озадачен данными ответами. Я имею в виду, что все они верны, но все отвечают на теоретическом или даже философском уровне, когда есть очень простой практический ответ на этот вопрос.

Если вы поместите return, break, continue или любое другое ключевое слово java, которое изменяет последовательное выполнение кода внутри блока catch (или даже блок try) , операторы внутри блока finally все еще будет выполнен.

Например:

public void myFunc() {

    double p = 1.0D;
    String str = "bla";
    try{
        p = Double.valueOf(str);
    }
    catch(Exception ex){
        System.out.println("Exception Happened");
        return;  //return statement here!!!
    }finally{
        System.out.println("Finally");
    }
    System.out.println("After finally");
}

при выполнении этого кода будет напечатано:

Exception Happened 
Finally

Это самая важная причина существования блока finally. Большинство ответов подразумевают это или ссылаются на это в сторонке, но ни один из них не подчеркивает это. Я думаю, потому что это своего рода вопрос новичка, такой прямой ответ очень важен.

46 голосов
/ 05 октября 2010

Я думаю, что код воли ближе всего подходит для выражения ключевой точки, и, вероятно, все это имеют в виду, но не совсем ясно.

Проблема в том, что действительно что-то не так с тем, что вы спрашиваете: «Если язапишите все операторы после блока catch вместо записи их в блок finally, тогда будет ли что-то не так? "

Если вы пишете все операторы после блока catch, вы подразумеваете, что

1) Вы всегда будете ловить исключение.

2) Вы всегда будете переходить к следующим операторам после того, как поймаете исключение.

Это означает, что вы всегда будете продолжать выполнение «нормально» после исключения, которое обычно является чем-то, что вы никогда на самом деле хотят делать.

Исключения должны быть именно такими - исключительными.Если вы действительно можете обработать исключение, всегда лучше написать свой код, чтобы сначала рассмотреть эти условия, а не привести к исключению вообще.Если вы следуете этой модели, то исключения являются действительно исключительными - условия, которые вы не могли предвидеть или, самое большее, не исправить.На самом деле не стоит ожидать того, к чему вы должны стремиться. В целом это означает, что вы не можете обрабатывать истинные исключения, что также означает, что вы не должны просто продолжать выполнение, часто вместо этого вы заканчиваете приложение.

Что обычносделано, вы позволяете ошибке распространяться обратно вверх по стеку вызовов.Некоторые говорят, что это сделано на случай, если кто-то, кто выше по цепочке, сможет справиться с этим.Я бы сказал, что по сути никогда не бывает, есть две реальные цели сделать это.Один из них может быть чем-то, что пользователь может исправить, если он есть.Таким образом, вы распространяете ошибку обратно до тех пор, пока не доберетесь до места, где сможете сообщить об этом пользователю.Или, во-вторых, пользователь не может это исправить, но вы хотите получить весь стек вызовов для отладки.Затем вы ловите его сверху, чтобы изящно провалиться.

Блок finally теперь должен иметь для вас больше значения.Как все говорят, это всегда работает.Самое ясное использование finally - это действительно попытка ... наконец, блок.Теперь вы говорите, что если код работает нормально, отлично.Нам все еще нужно выполнить некоторую очистку, и, наконец, всегда выполняется, затем мы идем дальше.Но если возникает исключение, теперь нам действительно нужен этот блок finally, потому что нам, возможно, все еще нужно будет выполнить некоторую очистку, но мы больше не ловим исключение здесь, поэтому мы больше не будем двигаться дальше.Блок finally важен для обеспечения очистки.

Идею исключения, всегда останавливающего выполнение, может быть трудно кому-то понять, пока у него не будет определенного опыта, но на самом деле это способвсегда делай вещиЕсли произошла ошибка, либо она была настолько незначительной, что вам следовало бы ее учесть с самого начала, либо появилось все больше и больше ошибок, ожидающих появления в будущем.

«Глотание» ошибок - их перехват идвигаться дальше - это худшее, что вы можете сделать, потому что ваша программа становится непредсказуемой, и вы не можете находить и исправлять ошибки.

Хорошо написанный код будет содержать столько блоков try ... finally, сколько необходимо, чтобы убедиться, что ресурсывсегда освобожден независимо от результата.Но хорошо написанный код, как правило, содержит лишь небольшое количество блоков try ... catch, которые существуют, прежде всего, для того, чтобы приложение могло как можно более грациозно завершиться с ошибкой или отложить на пользователя, что означает, по крайней мере, всегда передачу сообщения пользователю и т. Д.Но вы обычно не просто ловите ошибку и продолжаете идти.

41 голосов
/ 05 октября 2010

Основным моментом является то, что блок finally гарантированно будет выполнен, даже если исключение вызвано и не перехвачено . Затем вы используете блок finally, как единовременный шанс, для выполнения необходимой очистки, такой как закрытие потоков. Код после блока finally может никогда не быть достигнут.

Из учебника Java

Блок finally всегда выполняется, когда блок try выходит. Это гарантирует, что блок finally выполняется, даже если неожиданное исключение происходит. Но наконец-то полезно больше, чем просто обработка исключений - это позволяет программист, чтобы избежать очистки Код случайно обойден вернуться, продолжить или прервать Ввод код очистки в блоке finally всегда хорошая практика, даже когда нет ожидаются исключения.

10 голосов
/ 05 октября 2010

Если я понимаю вопрос, вы спрашиваете, в чем разница между:

try {
    Foo f = new Foo();
    f.bar();
}
finally
{
    Foo.baz();
}

И:

// This doesn't actually compile because a try block needs a catch and/or finally block
try {
    Foo f = new Foo();
    f.bar();
}
Foo.baz();

Или, более вероятно:

Foo f = new Foo();
f.bar();
Foo.baz();

Разница в том, что если new Foo() или f.bar() выдают исключение, блок finally будет выполнен в первом случае, но Foo.baz() не будет выполнен в двух последних случаях: вместо этого controlбудет пропускать Foo.baz(), пока JVM ищет обработчик исключений.


РЕДАКТИРОВАТЬ

Отвечая на ваш комментарий, как насчет:

Foo f = new Foo();
try {
    f.bar();
}
catch (Exception ex)
{
    // ...
}

f.baz();

Вы правы, если предположить, что блок catch не перебрасывает исключение или не возвращает метод, указывающий на произошедшую ошибку, тогда вызывается f.baz() независимо от того, было ли исключение.Однако даже в этом случае блок finally служит документацией о том, что для очистки используется f.baz().

Что еще более важно, тот факт, что исключение выдается, как правило, важен, поэтому его очень сложно написатькод, который продолжает делать то, что делал, не зная, что было сгенерировано исключение.Иногда исключения указывают на глупые вещи, которые вы можете игнорировать, и в этом случае вы должны проглотить исключение.Чаще, однако, вы захотите сообщить о сбое, либо перебрасывая исключение (или выбрасывая другое исключение), либо возвращаясь из метода с кодом ошибки.

Например, если предполагается, что f.bar()String преобразовать в Double, и в случае сбоя он выдаст NumberFormatException, затем код после блока try, вероятно, должен знать, что String фактически не был преобразован вDouble.И, как правило, вы не захотите продолжать после блока catch.Вместо этого вы захотите сообщить об ошибке.Это известно как «прерывание при отказе» (по сравнению с «возобновлением при отказе», которое, вероятно, следует называть «путаница после отказа со скрещенными пальцами»).

За исключением особых случаев, вы можетезапутаться.Например, блок catch может установить для Double значение Double.NaN, которое специально разработано для правильного распространения ошибок в математических выражениях.Даже в этом случае блок finally служит документацией о том, что f.baz() участвует в некоторой очистке.

3 голосов
/ 05 октября 2010

Блок finally содержит строки кода, которые должны выполняться независимо от того, было ли обнаружено исключение или нет. Даже если вы решите остановить выполнение кода в этом методе. Таким образом, код после t-c-f может не выполняться, но код finally «гарантирован» (гарантируется в том случае, если не происходит сбой, сразу же происходит ошибка, не подлежащая обработке).

2 голосов
/ 05 октября 2010

наконец, блок, особенно используемый во время предотвращения исключений.Если возникает какая-либо ошибка во время выполнения, программа может привести к завершению.Таким образом, в этот момент он вызовет блок finally, прежде чем завершить программу.Обычно 'finally' содержит операторы закрытия соединения, операции сохранения и ввода файлов, операции закрытия вывода.

2 голосов
/ 05 октября 2010

Да, было бы что-то очень критически неправильное.

И т. Е. Ваш код будет запускаться только , если есть ошибка .

Операторы внутри finally всегда запускается, независимо от того, какое исключение выдается.Вот в чем дело.

1 голос
/ 05 октября 2010

Если ваш код никогда не выдает исключение, или вы используете все исключения, которые будут правильными.Это не то, что всегда происходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...