Всегда ли выполняется блок finally в Java?
Учитывая этот код, могу ли я быть абсолютно уверен, что finally
блок всегда выполняется независимо от того, что something()
является?
try {
something();
return success;
}
catch (Exception e) {
return failure;
}
finally {
System.out.println("i don't know if this will get printed out.");
}
54 ответа
Да, finally
будет вызван после выполнения блоков кода try или catch.
Единственные времена finally
не будут называться:
- Если вы вызываете
System.exit()
; - Если JVM падает в первую очередь;
- Если JVM достигает бесконечного цикла (или некоторого другого непрерываемого, не заканчивающегося утверждения) в
try
или жеcatch
блок; - Если ОС принудительно завершает процесс JVM; например, "убить -9 " в UNIX.
- Если хост-система умирает; например, сбой питания, аппаратная ошибка, паника ОС и так далее.
- Если блок finally будет выполняться потоком демона, а все остальные потоки, не являющиеся демонами, завершатся до вызова finally.
Пример кода:
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
Кроме того, хотя это плохая практика, если в блоке finally есть оператор return, он превзойдет любой другой возврат из обычного блока. То есть следующий блок вернул бы false:
try { return true; } finally { return false; }
То же самое с выбрасыванием исключений из блока finally.
Вот официальные слова из спецификации языка Java.
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. Например, следующий код возвращает 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();
}
}
Вот разработка ответа Кевина. Важно знать, что возвращаемое выражение вычисляется до finally
, даже если он возвращается после.
public static void main(String[] args) {
System.out.println(Test.test());
}
public static int printX() {
System.out.println("X");
return 0;
}
public static int test() {
try {
return printX();
}
finally {
System.out.println("finally trumps return... sort of");
}
}
Выход:
X
finally trumps return... sort of
0
Я попробовал приведенный выше пример с небольшой модификацией
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
выход выполнен.
После выполнения finally
заблокировать try
Блок возвращает 2, а не 12, потому что этот оператор возврата не выполняется снова.
Если вы будете отлаживать этот код в Eclipse, у вас будет ощущение, что после выполнения System.out
из finally
заблокировать return
заявление о try
блок выполняется снова. Но это не так. Он просто возвращает значение 2.
Вот и вся идея окончательного блока. Это позволяет вам убедиться, что вы делаете очистки, которые в противном случае могли бы быть пропущены, потому что вы возвращаетесь, среди прочего, конечно.
Наконец, вызывается независимо от того, что происходит в блоке try (если вы не вызываете System.exit(int)
или виртуальная машина Java выходит из строя по какой-то другой причине).
Логичный способ думать об этом:
- Код, помещенный в блок finally, должен выполняться независимо от того, что происходит в блоке try
- Таким образом, если код в блоке try пытается вернуть значение или сгенерировать исключение, элемент помещается "на полку", пока блок finally не может быть выполнен
- Поскольку код в блоке finally имеет (по определению) высокий приоритет, он может возвращать или выдавать все, что пожелает. В этом случае все, что осталось "на полке", отбрасывается.
- Единственное исключение из этого, если виртуальная машина полностью отключается во время блока try, например, через "System.exit".
Наконец, всегда выполняется finally, если нет ненормального завершения программы (например, вызов System.exit(0)..). Итак, ваш системный будет напечатан
Нет, не всегда одним случаем исключения является // System.exit(0); прежде чем блок finally не позволяет выполнить, наконец, 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");
}
}
}
Блок finally всегда выполняется, если не происходит аварийного завершения программы, вызванного сбоем JVM или вызовом System.exit(0)
,
Кроме того, любое значение, возвращаемое из блока finally, будет переопределять значение, возвращаемое до выполнения блока finally, поэтому будьте осторожны при проверке всех точек выхода при использовании try finally.
Также возврат в, наконец, отбросит любое исключение. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
Наконец, всегда запускается, и в этом весь смысл, просто потому, что он появляется в коде после возврата, не означает, что так оно и реализовано. Среда выполнения Java отвечает за запуск этого кода при выходе из try
блок.
Например, если у вас есть следующее:
int foo() {
try {
return 42;
}
finally {
System.out.println("done");
}
}
Среда выполнения сгенерирует что-то вроде этого:
int foo() {
int ret = 42;
System.out.println("done");
return 42;
}
Если выброшено неисследованное исключение, finally
блок будет запущен, а исключение продолжит распространяться.
НЕ ВСЕГДА
Спецификация языка Java описывает, как блоки try-catch-finally и try-catch работают в 14.20.2
Ни в каком месте это не указывает, что блок finally всегда выполняется. Но для всех случаев, в которых завершаются блоки try-catch-finally и try-finally, указывается, что перед выполнением должны быть выполнены, наконец, finally.
try {
CODE inside the try block
}
finally {
FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).
JLS не гарантирует, что FIN выполняется после CODE. JLS гарантирует, что если выполняются CODE и NEXT, то FIN всегда будет выполняться после CODE и до NEXT.
Почему JLS не гарантирует, что блок finally всегда выполняется после блока try? Потому что это невозможно. Маловероятно, но возможно, что JVM будет прервана (kill, crash, power off) сразу после завершения блока try, но перед выполнением блока finally. JLS ничего не может сделать, чтобы избежать этого.
Таким образом, любое программное обеспечение, для которого его правильное поведение зависит от блоков finally, всегда выполняющихся после того, как завершены их блоки try.
Возвраты в блоке try не имеют отношения к этой проблеме. Если выполнение достигает кода после try-catch-finally, гарантируется, что блок finally будет выполнен раньше, с возвратами внутри блока try или без него.
Это потому, что вы присвоили значение i как 12, но не вернули значение i в функцию. Правильный код выглядит следующим образом:
public static int test() {
int i = 0;
try {
return i;
} finally {
i = 12;
System.out.println("finally trumps return.");
return i;
}
}
finally
всегда выполняется и перед возвратом x
(расчетное) значение.
System.out.println(foo());
....
int foo(){
int x = 2;
try{
return x++;
} finally{
System.out.println(x);
}
}
Выход:
3
2
Ответ прост ДА.
ВХОД:
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
Вкратце, в официальной документации Java (Нажмите здесь) написано, что -
Если JVM завершает работу во время выполнения кода try или catch, блок finally может не выполняться. Аналогично, если поток, выполняющий код try или catch, прерывается или уничтожается, блок finally может не выполняться, даже если приложение в целом продолжается.
Да, это будет называться. В этом весь смысл ключевого слова finally. Если выпрыгивание из блока try/catch может просто пропустить блок finally, это то же самое, что поместить System.out.println вне try/catch.
Потому что блок finally всегда будет вызываться, если вы не вызовете System.exit()
(или нить вылетает).
Добавление к ответу @vibhash, поскольку никакой другой ответ не объясняет, что происходит в случае изменяемого объекта, подобного приведенному ниже.
public static void main(String[] args) {
System.out.println(test().toString());
}
public static StringBuffer test() {
StringBuffer s = new StringBuffer();
try {
s.append("sb");
return s;
} finally {
s.append("updated ");
}
}
Будет выходной
sbupdated
Да, это будет. Независимо от того, что происходит в вашем блоке try или catch, если не вызывается System.exit() или не происходит сбой JVM. если есть какой-либо оператор возврата в блоке (ах), то, наконец, будет выполнен до этого оператора возврата.
Да, наконец, блок всегда выполняется. Большинство разработчиков используют этот блок для закрытия соединения с базой данных, объекта набора результатов, объекта оператора, а также использует в спящем режиме Java для отката транзакции.
Я попробовал это, это однопоточный.
class Test {
public static void main(String args[]) throws Exception {
Object obj = new Object();
try {
synchronized (obj) {
obj.wait();
System.out.println("after wait()");
}
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
Основной поток будет находиться в состоянии ожидания навсегда, поэтому, наконец, никогда не будет вызван,
поэтому вывод консоли не будет печатать строку: after wait()
или же finally
finally
выполнит и это точно.
finally
не будет выполняться в следующих случаях:
Случай 1:
Когда вы выполняете System.exit()
,
случай 2:
Когда ваш JVM / Thread падает.
случай 3:
Когда ваше выполнение остановлено между вручную.
Рассмотрим следующую программу:
public class someTest {
private static StringBuilder sb = new StringBuilder();
public static void main(String args[]) {
System.out.println(someString());
System.out.println("---AGAIN---");
System.out.println(someString());
}
private static String someString() {
try {
sb.append("-abc-");
return sb.toString();
} finally {
sb.append("xyz");
}
}
}
Начиная с Java 1.8.162, приведенный выше блок кода дает следующий вывод:
-abc-
---AGAIN---
-abc-xyz-abc-
это означает, что с помощью finally
Освободить объекты - хорошая практика, подобная следующей:
private static String someString() {
StringBuilder sb = new StringBuilder();
try {
sb.append("abc");
return sb.toString();
} finally {
sb = null;
}
}
Да, это будет. Единственный случай, когда это не будет, JVM выходит или вылетает
Это действительно так в любом языке...finally всегда будет выполняться перед оператором return, независимо от того, где этот возврат находится в теле метода. Если бы это было не так, блок finally не имел бы большого значения.
Я был очень смущен всеми ответами, предоставленными на разных форумах, и решил, наконец, написать код и посмотреть. Выход является:
Наконец, будет выполнен, даже если в блоке try и catch есть return.
try {
System.out.println("try");
return;
//int i =5/0;
//System.exit(0 ) ;
} catch (Exception e) {
System.out.println("catch");
return;
//int i =5/0;
//System.exit(0 ) ;
} finally {
System.out.println("Print me FINALLY");
}
Выход
пытаться
Напиши мне, наконец,
- Если возврат заменяется
System.exit(0)
в блоке try и catch в приведенном выше коде и по какой-либо причине перед ним возникает исключение.