Учитывая этот код, могу ли я быть абсолютно уверен в том, что блок 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");
}
31 ответ
Да, finally
будет вызываться после выполнения блоков кода try
или catch
.
finally
не будет вызван только в следующих случаях:
- Если вы вызовете
System.exit()
- Если вы вызовете
Runtime.getRuntime().halt(exitStatus)
- Если сначала произойдет сбой JVM
- Если JVM достигает бесконечного цикла (или какого-либо другого непрерываемого, не завершающего оператора) в блоке
try
илиcatch
- Если ОС принудительно завершает процесс JVM; например,
kill -9 <pid>
в UNIX - Если хост-система умирает; например, сбой питания, аппаратная ошибка, паника ОС и т. д.
- Если блок
finally
будет выполняться потоком демона, а все другие потоки, не являющиеся демонами, завершаются до вызоваfinally
thread.stop()
не обязательно предотвращает выполнение блока finally
.
finally
будет вызываться после блока try
, и до управление перейдет к следующим операторам. Это согласуется с блоком try, включающим бесконечный цикл, и, следовательно, блок finally никогда не вызывается.
Логичный способ подумать об этом:
- Код, помещенный в блок finally, должен выполняться независимо от того, что происходит в блоке try.
- Поэтому, если код в блоке try пытается вернуть значение или вызвать исключение, элемент помещается `` на полку '' до тех пор, пока блок finally не сможет выполнить
- Поскольку код в блоке finally имеет (по определению) высокий приоритет, он может возвращать или отбрасывать все, что угодно. В этом случае все, что осталось «на полке», выбрасывается.
- Единственное исключение из этого - если виртуальная машина полностью выключается во время блока попытки, например. от 'System.exit'
Finally всегда выполняется, если нет аварийного завершения программы (например, при вызове System.exit (0) ..). Итак, ваш sysout будет напечатан
Нет, не всегда одним исключением является // 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");
}
}
}
Наконец, всегда выполняется, в этом вся суть, просто потому, что он появляется в коде после возврата, не означает, что это реализовано именно так. Среда выполнения Java отвечает за запуск этого кода при выходе из блока try
.
Например, если у вас есть следующее:
int foo() {
try {
return 42;
}
finally {
System.out.println("done");
}
}
Среда выполнения сгенерирует что-то вроде этого:
int foo() {
int ret = 42;
System.out.println("done");
return 42;
}
Если генерируется неперехваченное исключение, то запускается блок finally
, и исключение продолжает распространяться.
Да, позвонят. В этом весь смысл ключевого слова finally. Если при выходе из блока try / catch можно было просто пропустить блок finally, это было то же самое, что поместить System.out.println за пределы try / catch.
Поскольку блок finally всегда будет вызываться, если вы не вызовете System.exit()
(или поток не завершится).
Рассмотрим следующую программу:
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.
}
}
sb.setLength(0)
?
sb = null;
просто добавляет ненужный код. Я понимаю, что вы имеете в виду, что блок finally
- хорошее место для освобождения ресурсов, таких как соединение с базой данных или что-то в этом роде, но имейте в виду, что ваш пример может сбить с толку новичков.
System.out.println("---AGAIN2---");
System.out.println(sb);
, и теперь это более понятно. Как бы то ни было, вывод противоречил вашему тезису: p Я также добавил к вашему ответу, но редактирование должно быть принято модератором или кем-то в этом роде. В противном случае вы можете добавить их
Блок finally
выполняется всегда и перед возвратом (вычисленного) значения x
.
System.out.println("x value from foo() = " + foo());
...
int foo() {
int x = 2;
try {
return x++;
} finally {
System.out.println("x value in finally = " + x);
}
}
Выход:
значение x в finally = 3
значение x из foo () = 2
В дополнение к пункту о возврате при окончательной замене возврата в блоке try то же самое верно и для исключения. Блок finally, который генерирует исключение, заменяет возврат или исключение, выданное из блока try.
Я был очень смущен всеми ответами, представленными на разных форумах, и решил наконец написать код и посмотреть. Выход:
finally будет выполнено, даже если в блоке try and catch есть возврат.
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");
}
Вывод
пытаться
Напечатайте меня НАКОНЕЦ
- Если return заменяется на
System.exit(0)
в блоке try and catch в приведенном выше коде и перед ним возникает исключение по любой причине.
Пример кода:
public static void main(String[] args) {
System.out.println(Test.test());
}
public static int test() {
try {
return 0;
}
finally {
System.out.println("something is printed");
}
}
Выход:
something is printed.
0
finally
на return 2;
не допускается (ошибка компилятора).
Кроме того, несмотря на то, что это плохая практика, если в блоке 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();
}
}
OutOfMemoryError
? ;)
return retVal
после блока finally
, хотя это, конечно, предполагает, что вы подавили некоторые другие исключения, потому что код иначе не имеет смысла.
Я пробовал приведенный выше пример с небольшими изменениями -
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.
i
был не примитивом, а целочисленным объектом.
В этом вся идея блока finally. Это позволяет вам убедиться, что вы выполняете очистку, которую в противном случае можно было бы пропустить из-за того, что вы вернетесь, среди прочего, конечно.
Наконец, вызывается независимо от того, что происходит в блоке 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;
}
}
Ответ простой ДА .
ВВОД:
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 всегда выполняется. Большинство разработчиков используют этот блок для закрытия соединения с базой данных, объекта набора результатов, объекта оператора, а также используют его в спящем режиме java для отката транзакции.
Вкратце, в официальной документации Java (щелкните здесь) написано, что -
Если JVM завершает работу во время выполнения кода try или catch, то блок finally может не выполняться. Аналогичным образом, если поток, выполняющий код try или catch, прерывается или завершается, блок 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 и до ДАЛЕЕ .
Почему JLS не гарантирует, что блок finally
всегда выполняется после блока try
? Потому что это невозможно. Маловероятно, но возможно, что JVM будет прервана (уничтожение, сбой, отключение питания) сразу после завершения блока try
, но до выполнения finally
блокировать. JLS ничего не может сделать, чтобы этого избежать.
Таким образом, любое программное обеспечение, правильное поведение которого зависит от того, чтобы блоки finally
всегда выполнялись после того, как их блоки try
были завершены, обнаруживаются ошибки.
Инструкции return
в блоке try
не имеют отношения к этой проблеме. Если выполнение достигает кода после try
- catch
- finally
, гарантируется, что блок finally
будет выполнен раньше, с инструкциями return
внутри или без них. блок try
.
any software which for their proper behaviour depends on finally blocks always being executed after their try blocks complete are bugged.
правильно. Блоки finally
обычно используются для очистки ресурсов, чтобы предотвратить утечки. Как еще вы это сделали бы? Точно так же вы не можете (или не должны ...) ловить Error
s, поскольку (обычно) нет разумного способа для нормального приложения их обработать.
finally
может (при наличии достаточного количества времени, вероятно, будет ) вызвать ошибки, но дополнительное время на разработку (и тестирование) стоит потенциально незаметного улучшения стабильности. ? В более общем плане: зависит ли количество внимания, которое должно быть уделено конкретному проекту, от рассматриваемой подсистемы? В конце концов, время - ценный ресурс. :)
Да, это будет. Независимо от того, что происходит в вашем блоке try или catch, если только не вызывается System.exit () или JVM не аварийно завершается. если в блоке (ах) есть какой-либо оператор возврата, он будет выполнен до этого оператора возврата.
Да, это будет. Только в том случае, если этого не произойдет, это выход из JVM или сбой
На самом деле это верно для любого языка ... finally всегда будет выполняться перед оператором return, независимо от того, где это return находится в теле метода. Если бы это было не так, блок finally не имел бы большого значения.
Добавление к ответу @ 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
Я пробовал это, он однопоточный.
public static void main(String args[]) throws Exception {
Object obj = new Object();
try {
synchronized (obj) {
obj.wait();
System.out.println("after wait()");
}
} catch (Exception ignored) {
} finally {
System.out.println("finally");
}
}
main
Thread
всегда будет в состоянии wait
, поэтому finally
никогда не будет вызываться ,
Поэтому консольный вывод не будет print
String
: после wait()
или finally
Согласованный с @Stephen C, приведенный выше пример является одним из 3-го упоминания здесь:
Добавляем еще несколько таких возможностей бесконечного цикла в следующий код:
// import java.util.concurrent.Semaphore;
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 ignored) {
} finally {
System.out.println("finally");
}
}
Случай 2: Если сначала произойдет сбой JVM
import sun.misc.Unsafe;
import java.lang.reflect.Field;
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
будет выполняться демоном Thread
, а все остальные не-демоны Threads
выйдут до вызова finally
.
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
finally
будет выполняться, и это точно.
finally
не будет выполняться в следующих случаях:
Случай 1 :
Когда вы выполняете System.exit()
.
Случай 2:
Когда ваша JVM / Thread выходит из строя.
Случай 3:
Когда ваше выполнение остановлено вручную.
Также возврат в finally отбрасывает любое исключение. http://jamesjava.blogspot.com/2006/03 /dont-return-in-finally-clause.html
Блок finally всегда выполняется, если не происходит аварийного завершения программы, либо в результате сбоя JVM, либо в результате вызова System.exit(0)
.
Кроме того, любое значение, возвращаемое из блока finally, переопределит значение, возвращенное до выполнения блока finally, поэтому будьте осторожны при проверке всех точек выхода при использовании try finally.
Вот уточнение ответа Кевина. Важно знать, что возвращаемое выражение оценивается до 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
finally
. Вычисление возвращаемого значения (здесь printX()
) все еще предшествует этому.
System.out.println("finally trumps return... sort of");
на System.out.print("finally trumps return in try"); return 42;
return
не возвращает какое-то волшебное продолжение, которое оценивается только в том случае, если вызывающий абонент печатает его или что-то еще. printX()
вызывается до того, как произойдет return
, независимо от try
/catch
или чего-либо еще.
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.
probably
.finally
; finalizer == методfinalize()
.