2016-04-26 2 views
21

Сегодня в колледже мы немного поговорили о 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(); 

Этот фрагмент кода после улова всегда будет выполнить.

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

+3

Что делать, если ваш улов сделал что-то вроде броска другого (возможно, неконтролируемого) исключения? Будет ли 'out.close()' запускаться в этой ситуации? Другими словами, просто печать трассировки стека и перемещение происходит не всегда, как обрабатывается исключение. – rmlan

+3

Если вы повторно выбросите исключение или не поймаете все это, тогда блок finally станет вашим другом. Существуют также Тихие условия, которые не являются исключениями, а затем второй пример будет проблематичным. – KevinO

+0

Я собираюсь начать узнавать о Throwable, поэтому, я думаю, мне нужно будет сначала изучить это, чтобы понять это, не так ли? –

ответ

33

Это все равно будет иметь значение, если код выбрасывает 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.

+0

Не знал, что ошибки могут произойти в блоке catch. Спасибо, теперь это имеет смысл. –

+15

@MiljanRakita Ошибки могут произойти где угодно. Его часть удовольствия! – Gusdor

+2

Имейте в виду, что 'finally' будет * не * выполняться в действительно катастрофических ситуациях (скажем, сбое питания), поэтому не рассчитывайте на него для таких вещей, как поддержание последовательности транзакций базы данных. – Mark

3

При возникновении неперехваченной ошибки в try блоке, или даже если ошибка происходит в вашем catch блоке, «кусок коды» после улова не будет выполнен, но finally блока будет.

finally всегда будет выполнен.

Из документации Java:

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

2

Что делать, если что-то в блоке catch будет выбрасывать исключение? out.close не будет выполняться. Вы также можете использовать «try with resources», чтобы убедиться, что все ресурсы закрыты после его использования.Попробуйте этот пример:

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(); 
    } 
+0

Отлично! Большое спасибо ! –

1

Ваш второй пример может бросить ненужную NullPointerException, если исключение происходит в застройщик из PrintWriter.

Другая возможность заключается в том, что out.close(); выдает ошибку, которая не была обнаружена.

Если вы переместите свой код в блок finally, он всегда будет выполнен - ​​независимо от того, насколько успешный или успешный блок try. Это особенно полезно, если ваш try -block вызывает исключение, то есть не пойман. В вашем втором примере это приведет к тому, что out.close() не будет выполнен, тогда как с блоком finally он будет выполнен, даже если блок try выдает неперехваченную ошибку.

+0

Большое вам спасибо. Не знал, что можно выбросить исключение! –

+1

Даже в первом примере NPE будет выброшен. Лучше всего использовать try-with-resources. – Axel

+0

Да, попробовать-с-ресурсами лучше, но вопрос был о 'finally', поэтому я не упоминал об этом. – Polygnome

2

Обычная usecase, в конце концов, заключается в том, что вы не хотите поймать исключение в том же методе.

В этом случае вы используете попытку с блоком finally без улова. Таким образом, вы можете гарантировать, что ваши ресурсы будут закрыты, не получая исключения в самом методе.

1

Хотя сами по себе эти два примера 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()); 
    } 
} 

Обе программы вывода .

Причина этого может быть найден на Chapter 14.20.2 of JLS

заявление попытки с наконец блоком выполняются первым выполнение блока Try. Тогда есть выбор:

• Если выполнение блока Ьги завершается преждевременно из-за броска величины V, то есть выбор:
[...]
- Если пробег -time тип V не является присвоением, совместимым с catchable классом исключений любого предложения catch оператора try, тогда выполняется окончательный блок . Тогда есть выбор:
[...]
>Если, наконец, блок завершается преждевременно по причине S, тогда оператор попытка завершается преждевременно по причине S (и бросок значения V отбрасывается и забыли).

Если выполнение блока Ьгу завершается преждевременно по любой другой причине R, то, наконец, блок выполняется, а затем есть выбор:
- Если наконец блок завершается нормально, то утверждение TRY завершает по ошибке R.
- Если блок finally завершается внезапно по причине S, то инструкция try завершается внезапно по причине S (и причина R отбрасывается).

Редактирование шахты

Рассмотрим, что return скачкообразное способ завершения блока try или finally.

2

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

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

0

меньший пример:

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 частичные выбросы IOException, который улавливается и регистрируются, то finally пробегов блока, а затем do_something_else() или
  • try часть бросает другие метательную, который не поймали, но finally работает блок, то код переходит к следующим ограждающим try , возможно, в вызывающем абоненте.

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