Является ли блок 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");
}
Ответы
Ответ 1
Да, finally
будет вызываться после выполнения кодовых блоков try
или catch
.
Единственные случаи, когда finally
не будут вызваны:
- Если вы вызываете
System.exit()
- Если JVM падает сначала
- Если JVM достигает бесконечного цикла (или некоторого другого непрерываемого, не завершающего оператора) в блоке
try
или catch
- Если ОС принудительно завершает процесс JVM; например,
kill -9 <pid>
в UNIX
- Если хост-система умирает; например, сбой питания, аппаратная ошибка, паника ОС и т.д.
- Если блок
finally
будет выполняться потоком демона и все остальные потоки, не являющиеся демонами, завершатся до вызова finally
Ответ 2
Пример кода:
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
Ответ 3
Кроме того, хотя это плохая практика, если в блоке finally есть оператор return, он будет превзойти любой другой возврат из обычного блока. То есть, следующий блок вернет false:
try { return true; } finally { return false; }
То же самое, что бросает исключения из блока finally.
Ответ 4
Вот официальные слова из спецификации языка 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) ;
A return
без Expression
пытается передать управление вызывающему методу или конструктору, который его содержит.
A return
с помощью Expression
попыток передать управление вызывающему методу, который содержит его; значение Expression
становится значением вызова метода.
В предыдущих описаниях говорится, что " пытается передать управление", а не просто "передает управление", потому что, если в методе или конструкторе есть какие-либо try
-операторы, чьи блоки try
содержат return
, то любые предложения finally
этих операторов try
будут выполняться, чтобы быть наиболее внутренним до внешнего, до того как управление будет передано вызову метода или конструктора. Резкое завершение предложения finally
может нарушить передачу управления, инициированного оператором return
.
Ответ 5
В дополнение к другим ответам важно указать, что "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();
}
}
Ответ 6
Я попробовал приведенный выше пример с небольшой модификацией -
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
of finally
блокирует оператор return
блока try
выполняется снова. Но это не так. Он просто возвращает значение 2.
Ответ 7
Вот разработка ответа Кевина. Важно знать, что возвращаемое выражение оценивается до 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
Ответ 8
Вот и вся идея окончательного блока. Он позволяет вам убедиться, что вы выполняете очистку, которая в противном случае может быть пропущена, потому что вы, конечно же, возвращаетесь, между прочим.
Наконец, вызывается независимо от того, что происходит в блоке try (если вы не вызываете System.exit(int)
или виртуальная машина Java не запускается по какой-либо другой причине).
Ответ 9
Логический способ подумать об этом:
- Код, помещенный в блок finally, должен быть выполнен независимо от того, что происходит в блоке try
- Итак, если код в блоке try пытается вернуть значение или выбросить исключение, элемент будет помещен "на полку" до тех пор, пока блок finally не сможет выполнить
- Поскольку код в блоке finally имеет (по определению) высокий приоритет, он может возвращать или бросать все, что ему нравится. В этом случае все, что осталось на полке, отбрасывается.
- Единственным исключением из этого является то, что VM полностью отключается во время блока try, например. по 'System.exit'
Ответ 10
наконец, всегда выполняется, если нет ненормального завершения программы (например, вызов System.exit(0)..). поэтому ваш sysout будет напечатан
Ответ 11
Также возвращение в итоге выкинет любое исключение. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
Ответ 12
Блок finally всегда выполняется, если нет аномального завершения программы, вызванного сбоем JVM или вызовом System.exit(0)
.
Кроме того, любое значение, возвращаемое из блока finally, переопределит значение, возвращаемое до выполнения блока finally, поэтому будьте осторожны при проверке всех точек выхода при окончательном использовании try.
Ответ 13
Нет, не всегда один случай исключения //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");
}
}
}
Ответ 14
Наконец, всегда выполняется, что вся точка, только потому, что она появляется в коде после возврата, не означает, что это было реализовано. Среда выполнения Java несет ответственность за запуск этого кода при выходе из блока try
.
Например, если у вас есть следующее:
int foo() {
try {
return 42;
}
finally {
System.out.println("done");
}
}
Среда выполнения создаст что-то вроде этого:
int foo() {
int ret = 42;
System.out.println("done");
return 42;
}
Если вызывается неперехваченное исключение, будет выполняться блок finally
, и исключение будет продолжать распространяться.
Ответ 15
Это связано с тем, что вы назначили значение я как 12, но не возвращали значение я в функцию. Правильный код выглядит следующим образом:
public static int test() {
int i = 0;
try {
return i;
} finally {
i = 12;
System.out.println("finally trumps return.");
return i;
}
}
Ответ 16
Поскольку блок finally всегда будет вызываться, если вы не вызываете System.exit()
(или поток не работает).
Ответ 17
Ответ прост ДА.
INPUT:
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
Ответ 18
Да, он будет вызван. Это весь смысл наличия ключевого слова finally. Если выпрыгнуть из блока try/catch можно просто пропустить блок finally, это было бы то же самое, что и поставить System.out.println вне try/catch.
Ответ 19
Вкратце, в официальной документации Java (нажмите здесь), написано, что -
Если JVM завершает работу, пока выполняется код try или catch, тогда блок finally может не выполняться. Аналогично, если выполнение потока код try или catch прерывается или убивается, блок finally может не выполняются, даже если приложение в целом продолжается.
Ответ 20
Да, блок finally всегда выполняется. Большинство разработчиков используют этот блок для закрытия соединения с базой данных, объекта результатов, объекта-оператора и также использования в спящем режиме java для отката транзакции.
Ответ 21
Да, будет. Независимо от того, что происходит в вашем блоке try или catch, если иное не вызвано вызовом System.exit() или JVM. если в блоке (-ах) есть какой-либо оператор возврата, он, наконец, будет выполнен до этого оператора return.
Ответ 22
Да, будет.
Только в случае, если это не произойдет, JVM выйдет или сработает
Ответ 23
Добавляем к @vibhash answer, поскольку никакой другой ответ не объясняет, что происходит в случае изменяемого объекта, такого как ниже.
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
Ответ 24
Рассмотрим следующую программу:
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());
System.out.println("---PRINT THE RESULT---");
System.out.println(sb.toString());
}
private static String someString() {
try {
sb.append("-abc-");
return sb.toString();
} finally {
sb.append("xyz");
}
}
}
Начиная с Java 1.8.162, приведенный выше блок кода дает следующий вывод:
-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz
это означает, что использование finally
для освобождения объектов является хорошей практикой, подобной следующему коду:
private static String someString() {
StringBuilder sb = new StringBuilder();
try {
sb.append("abc");
return sb.toString();
} finally {
sb = null; // Just an example, but you can close streams or DB connections this way.
}
}
Ответ 25
То, что на самом деле истинно на любом языке..., наконец, всегда будет выполняться перед оператором return, независимо от того, где это возвращение находится в теле метода. Если бы это было не так, блок finally не имел бы большого значения.
Ответ 26
В дополнение к вопросу о возврате в окончательной замене возврата в блоке try то же самое относится к исключению. Блок finally, который генерирует исключение, заменит возврат или исключение, вызванное из блока try.
Ответ 27
finally
будет выполняться, и это точно.
finally
не будет выполняться в следующих случаях:
случай 1:
Когда вы выполняете System.exit()
.
случай 2:
При сбое вашего JVM/Thread.
случай 3:
Когда ваше выполнение остановлено между вручную.
Ответ 28
НЕ ВСЕГДА
Спецификация языка Java описывает, как блоки try-catch-finally и try-catch работают в 14.20.2
Ни в каком месте это не указывает, что блок finally всегда выполняется.
Но для всех случаев, в которых завершаются блоки try-catch-finally и try-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 выполняется после КОДА.
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.
Ответ 29
Я попробовал это,
Это однопоточный.
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
Согласившись с @Stephen C, приведенный выше пример является одним из упоминаний третьего случая здесь:
Добавим еще несколько таких возможностей бесконечного цикла в следующем коде:
// import java.util.concurrent.Semaphore;
class Test {
public static void main(String[] args) {
try {
// Thread.sleep(Long.MAX_VALUE);
// Thread.currentThread().join();
// new Semaphore(0).acquire();
// while (true){}
System.out.println("after sleep join semaphore exit infinite while loop");
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
Случай 2. Если JVM аварийно завершает работу
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Test {
public static void main(String args[]) {
try {
unsafeMethod();
// Runtime.getRuntime().halt(123);
System.out.println("After Jvm Crash!");
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
unsafe.putAddress(0, 0);
}
}
Ссылка: Как вы разбили JVM?
Случай 6: если блок finally будет выполняться потоком демона, и все другие потоки, не являющиеся демонами, завершат работу до вызова finally.
class Test {
public static void main(String args[]) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
printThreads("Daemon Thread printing");
// just to ensure this thread will live longer than main thread
Thread.sleep(10000);
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
};
Thread daemonThread = new Thread(runnable);
daemonThread.setDaemon(Boolean.TRUE);
daemonThread.setName("My Daemon Thread");
daemonThread.start();
printThreads("main Thread Printing");
}
private static synchronized void printThreads(String str) {
System.out.println(str);
int threadCount = 0;
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (Thread t : threadSet) {
if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
System.out.println("Thread :" + t + ":" + "state:" + t.getState());
++threadCount;
}
}
System.out.println("Thread count started by Main thread:" + threadCount);
System.out.println("-------------------------------------------------");
}
}
вывод: это не выводит "finally", что означает, что "блок finally" в "потоке демона" не выполнялся
main Thread Printing
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED
Thread :Thread[main,5,main]:state:RUNNABLE
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE
Thread count started by Main thread:3
-------------------------------------------------
Daemon Thread printing
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE
Thread count started by Main thread:2
-------------------------------------------------
Process finished with exit code 0
Ответ 30
- Наконец, блок всегда выполняется. Пока и до
Существует System.exit() (первый оператор в блоке finally).
- Если system.exit() - это первый оператор, то, наконец, блок не будет выполнен, а управление будет выведено из блока finally.
Всякий раз, когда оператор System.exit() получает окончательный блок до тех пор, пока этот оператор окончательно не будет выполнен, а когда появится System.exit(), тогда из блока finally полностью выйдет управляющее усилие.