java - когда - при каких условиях блок finally не будет выполнен




Является ли блок finally всегда исполняемым на Java? (20)

Учитывая этот код, могу ли я быть абсолютно уверенным, что блок finally всегда выполняется, вне зависимости от того, something() что- something() ?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("i don't know if this will get printed out.");
}

Ответ прост ДА .

ВХОД:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

ВЫХОД:

catch
finally

Блок finally всегда выполняется, если нет аномального завершения программы, вызванного сбоем JVM или вызовом System.exit(0) .

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


В дополнение к другим ответам важно указать, что «наконец» имеет право переопределить любое исключение / возвращаемое значение блоком try..catch. Например, следующий код возвращает 12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

Аналогичным образом, следующий метод не генерирует исключения:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

Хотя следующий метод действительно бросает его:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

Вкратце, в официальной документации Java (нажмите here ) написано, что -

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


Вот официальные слова из спецификации Java Language Specification.

14.20.2. Выполнение try-finally и try-catch-finally

Оператор try с блоком finally выполняется первым выполнением блока try . Тогда есть выбор:

  • Если выполнение блока try завершается нормально, [...]
  • Если выполнение блока try завершается внезапно из-за throw значения V , [...]
  • Если выполнение блока try завершается внезапно по любой другой причине R , тогда выполняется блок finally . Тогда есть выбор:
    • Если блок finally завершается нормально, то оператор try внезапно завершается по причине R.
    • Если блок finally завершается внезапно для разума S , то оператор try внезапно завершается по причине S ( и причина R отбрасывается ).

Спецификация return фактически делает это явным:

JLS 14.17 Оператор возврата

ReturnStatement:
     return Expression(opt) ;

Оператор return без Expression пытается передать управление вызывающему методу или конструктору, который его содержит.

Оператор return с Expression пытается передать управление вызывающему методу, который содержит его; значение Expression становится значением вызова метода.

В предыдущих описаниях сказано « попытки передать управление », а не просто « управление передачей », потому что, если в методе или конструкторе есть какие-либо утверждения try чьи блоки try содержат оператор return , тогда любые finally предложения этих операторов try будут выполняться в порядок, самый внутренний до внешнего, до того, как управление передается вызывающему методу или конструктору. Резкое завершение предложения finally может привести к нарушению передачи управления, инициированного оператором return .


Да, finally , вызывается после выполнения блоков try или catch.

Единственными моментами, которые, finally , не будут называться, являются:

  1. Если вы вызываете System.exit() ;
  2. Если сначала произойдет сбой JVM;
  3. Если JVM достигает бесконечного цикла (или другого не прерывающегося, не заканчивающегося оператора) в блоке try или catch ;
  4. Если ОС принудительно завершает процесс JVM; например, «kill -9» в UNIX.
  5. Если хост-система умирает; например, сбой питания, аппаратная ошибка, паника ОС и т. д.
  6. Если, наконец, блок будет выполняться потоком демона, а все остальные не-демонные потоки выходят до того, как, наконец, вызывается.

Да, он будет вызван. В этом весь смысл наличия ключевого слова finally. Если выпрыгнуть из блока try / catch можно просто пропустить блок finally, это было бы то же самое, что и поставить System.out.println вне try / catch.


Да, это будет. Независимо от того, что происходит в вашем блоке try или catch, если иное не вызвано вызовом System.exit () или JVM. если в блоке (-ах) есть какой-либо оператор возврата, он, наконец, будет выполнен до этого оператора return.


Если исключение выбрасывается, он, наконец, запускается. Если исключение не выбрасывается, оно, наконец, запускается. Если исключение поймано, он, наконец, запускается. Если исключение не поймано, он, наконец, запускается.

Только время, когда он не запускается, - это когда JVM завершает работу.



Логичным способом думать об этом является:

  1. Код, помещенный в блок finally, должен быть выполнен независимо от того, что происходит в блоке try
  2. Поэтому, если код в блоке try пытается вернуть значение или выбросить исключение, элемент будет помещен «на полку» до тех пор, пока блок finally не сможет выполнить
  3. Поскольку код в блоке finally имеет (по определению) высокий приоритет, он может возвращать или бросать все, что ему нравится. В этом случае все, что осталось на полке, отбрасывается.
  4. Единственным исключением из этого является то, что VM полностью отключается во время блока try, например, «System.exit»

Логичным способом думать об этом является:

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

Поэтому, если код в блоке try пытается вернуть значение или исключить исключение, элемент помещается «на полку» до тех пор, пока блок finally не сможет выполнить. Поскольку код в блоке finally имеет (по определению) высокий приоритет, он может возвращаться или бросать независимо от того, что ему нравится. В этом случае все, что осталось на полке, отбрасывается.

Единственным исключением из этого является то, что VM полностью отключается во время блока try, например, «System.exit»

Никогда не бросайте исключение из блока finally

try {
  someMethod();  //Throws exceptionOne
} finally {
  cleanUp();    //If finally also threw any exception the exceptionOne will be lost forever
}

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

Фактически, выходим из программы (либо вызывая System.exit (), либо вызывая фатальную ошибку, из-за которой процесс прерывается: иногда он упоминается неформально как «горячая точка» или «Dr Watson» в Windows) предотвратит ваш окончательный блок выполнено!

Нет ничего, что помешало бы нам встраивать блоки try / catch / finally (например, класть блок try / finally внутри блока try / catch или наоборот), и это не такая необычная вещь.


Наконец, это всегда работает, и это все, только потому, что оно появляется в коде после возвращения, не означает, что так оно и было реализовано. Среда выполнения Java несет ответственность за запуск этого кода при выходе из блока try .

Например, если у вас есть следующее:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

Среда выполнения создаст что-то вроде этого:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

Если выбрано неперехваченное исключение, блок finally будет запущен, и исключение продолжит распространение.


Нет, не всегда один случай исключения // System.exit (0); прежде чем блок finally предотвратит, наконец, выполнение.

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

Потому что окончательный всегда вызывается в любых случаях, которые у вас есть. У вас нет исключения, оно все еще называется, исключение catch, оно все еще называется


Пример кода:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

Выход:

finally trumps return. 
0

Рассмотрите это в обычном ходе выполнения (т. Е. Без какого-либо исключения): если метод не является «void», он всегда явно возвращает что-то, но, наконец, всегда выполняется


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

Наконец, вызывается независимо от того, что происходит в блоке try ( если вы не вызываете System.exit(int) или виртуальную машину Java не запускается по какой-либо другой причине).


Это связано с тем, что вы назначили значение i как 12, но не возвращали значение i в функцию. Правильный код выглядит следующим образом:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}

Я попробовал приведенный выше пример с небольшими изменениями -

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

Вышеуказанные выходы кода:

наконец, козыри возвращаются.
2

Это связано с тем, что при return i; выполняется i имеет значение 2. После этого выполняется блок finally где 12 назначается i а затем выполняется System.out out.

После выполнения блока finally блок try возвращает 2, а не возвращает 12, потому что этот оператор возврата не выполняется снова.

Если вы отлаживаете этот код в Eclipse, тогда вы почувствуете, что после выполнения System.out finally блока оператор return будет выполнен снова. Но это не так. Он просто возвращает значение 2.





try-catch-finally