Всегда ли выполняется блок 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");
}

В противном случае ключевое слово следует называть probably.

Noon Silk 13.05.2010 10:18
Не всегда
Boann 23.09.2013 04:52

Эффективная Java говорит иначе informit.com/articles/article.aspx?p=1216151&seqNum=7

Binoy Babu 14.12.2014 06:08

@BinoyBabu, финализатор! = finally; финализатор == метод finalize().

jaco0646 12.07.2016 19:11

@Boann Правильно, «не всегда». Но тогда вы можете использовать никогда и никогда слова «гарантировано» или «всегда».

MC Emperor 19.01.2017 22:40

@Boann Я бы сказал так: поток выполнения всегда проходит окончательно, прежде чем он ускользнет из структуры try-finally. Если он умирает внутри, меня это устраивает, потому что основная цель finally - убедиться, что что-то не испортилось для других частей кода.

Imperishable Night 27.10.2018 00:02

Похоже, вы там неплохо поймали!

Adrian M. 28.05.2020 04:07

Если блок finally возвращает значение, то блок try / catch не может вернуться.

Vishnu Haridas 22.09.2020 20:17
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2 484
8
530 251
49
Перейти к ответу Данный вопрос помечен как решенный

Ответы 49

Ответ принят как подходящий

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

finally не будет вызван только в следующих случаях:

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

На самом деле thread.stop() не обязательно предотвращает выполнение блока finally.

Piotr Findeisen 31.03.2011 01:12

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

Andrzej Doyle 16.09.2011 15:24

Согласно определению @andrzejDoyle, он сначала вернет success, прежде чем перейти к блоку finally. Это верно?

dieend 21.04.2013 11:44

@dieend: он решит, что возвращаемое значение будет успешным, выполнит блок finally, а затем вернет управление вызывающей функции. Ничего не будет возвращено, если блок finally сработает.

Laurent LA RIZZA 28.06.2013 09:00

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

ruhungry 23.03.2014 00:39

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

Amrish Pandey 11.09.2014 16:07

Но эффективная Java говорит об обратном. informit.com/articles/article.aspx?p=1216151&seqNum=7

Binoy Babu 14.12.2014 06:08

Это довольно интересно, что, наконец, будет вызвано, когда кто-то повторно выбрасывает свое исключение в catch () как новое исключение RuntimeException.

Mikhail Krutov 16.08.2015 03:22

@BinoyBabu - Это про финализатор, а не окончательный блок

avmohan 05.01.2017 13:17

А что насчет Время выполнения # остановка?

gstackoverflow 13.03.2017 11:35

@AmrishPandey "блок finally не вызывается в случае исключения, сгенерированного потоком демона" - правда ?? [Требуется цитата], я думаю?

SusanW 25.04.2017 15:59

@SusanW javarevisited.blogspot.in/2012/03/…

Amrish Pandey 05.06.2017 19:43

@SusanW docs.oracle.com/javase/tutorial/essential/exceptions/…

Amrish Pandey 05.06.2017 19:45

@AmrishPandey На странице Oracle демоны вообще не упоминаются. Другая страница (блоггера) обсуждает JVM прекращение, но не демонстрирует каких-либо различий по сравнению с потоком, не являющимся демоном. Реальность такова: при завершении System.exit (включая перехватчики выключения) или другом завершении все потоки просто умирают на том месте, где они стоят, будь то демон или нет: в любом случае окончательные операции не выполняются. Блогер ошибается, когда замечает это как разницу ... попробуйте сами.

SusanW 05.06.2017 21:21

@SusanW - Верно. Причина, по которой поток демона не выполняет finally в этом примере, - это причина 3). Это бесконечный цикл. Если цикла не было, то он также мог не выполнить finally по причине 1). Когда метод main возвращается (и нет потоков, не являющихся демонами), он выполняет System.exit() ... или эквивалент. Итог: потоки демонов НЕ являются исключением из причин, указанных в ответе выше.

Stephen C 20.08.2017 04:16

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

Loduwijk 03.01.2018 18:17

«Хост-система умирает; например, сбой питания, аппаратная ошибка ...» также не является исключением из правила окончательного блока. Это похоже на "будет ли return 1+1 всегда возвращать 2?" «Не обязательно; потеря питания может помешать return 1+1 вернуться 2.» ... не совсем часть ответа. То же самое с №4, пример kill -9; для №4 и №5 достаточно, чтобы был указан №2 «Сбой JVM», который можно было бы сформулировать так: «JVM завершает работу внезапно», чтобы лучше отловить все ситуации.

Loduwijk 03.01.2018 18:20

finally также выполняется, когда выбрасывается StackOverflowError.

MC Emperor 28.02.2018 18:00

Java System.exit(); (например, C# или PHP) может сбивать с толку разработчиков, пришедших с других языков программирования. В Python sys.exit() вызывает (обычное) исключение SystemExit, которое можно перехватить. В Паскале exit - это обычный управляющий оператор, и finally тоже будет выполняться.

Dávid Horváth 03.05.2018 17:04

@MCEmperor, чтобы воспользоваться этим, хотя finally выполняется даже после StackOverflowError, кадры, добавленные в стек в блоке finally, могут вызвать неожиданный дополнительныйStackOverflowError, что приведет к пропуску оставшегося содержимого блока finally. По этой причине ошибки StackOverflowError могут быть довольно опасными. Недавно мы столкнулись с трудной проблемой, когда семафоры не выпускались в блоке finally именно по этой причине.

Mike Hill 01.08.2018 18:15

В: Всегда ли? A: Да, за исключением ... Для меня это означает, что принятый ответ должен быть отрицательным. Если есть исключения из всегда, то их нет. Он может работать большую часть времени. Но не всегда. Я удивлен, что я один из немногих, кто отреагировал на это.

JugsteR 14.08.2018 10:27

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

Captain Man 13.09.2018 21:26

Если машина приостановлена. Если ядро ​​больше никогда не дает JVM время для выполнения. Вероятно, есть еще много крайних случаев, когда finally не выполняется. Этот ответ дает хорошее и легкое для понимания объяснение. Но было бы проще и проще сказать «Если блок try или блок try-catch завершается, то выполняется блок finally». JLS использует тот же самый язык.

Anonymous Coward 25.10.2018 02:38

Еще один: когда я отлаживаю с помощью IntelliJ, если я отбрасываю кадр стека внутри блока try (который также имеет связанный блок finally), IntelliJ спрашивает меня, хочу ли я выполнить блок finally или нет. Я не уверен, что это стандартная функция отладчика Java или что-то, что IntelliJ разработал самостоятельно.

kevinarpe 10.12.2020 15:17

finally всегда выполняется, если нет аварийного завершения программы (например, вызов System.exit (0) ..). Итак, ваш sysout будет напечатан

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

К вашему сведению: в C# поведение идентично, за исключением того факта, что замена оператора в предложении finally на return 2; не допускается (ошибка компилятора).

Alexander Pacha 31.10.2013 12:08

Вот важная деталь, о которой следует помнить: stackoverflow.com/a/20363941/2684342

WoodenKitty 04.12.2013 03:37

ЕСЛИ ВЫ ДОБАВИЛИ БЛОК ЗАХВАТА, error: missing return statement

Harry 26.10.2014 08:20

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

Zyl 15.04.2015 20:25

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

Trimtab 21.06.2016 07:20

@Trimtab, да, это только ответ на вопрос как заданный. Ответ Тристана поясняет, что возвращаемое значение оценивается, блок finally запускается, а затем управление возвращается вызывающей стороне.

Kevin 21.06.2016 23:02

Извините, но это демонстрация, а не доказательство. Это только доказательство, если вы можете показать, что этот пример всегда ведет себя таким образом на всех платформах Java, И что аналогичные примеры также всегда ведут себя таким образом.

Stephen C 20.08.2017 04:24

Кстати, не по теме: я политически уверен, что этот оператор print будет стареть, как молоко.

Julio Cezar Silva 14.08.2020 19:20

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

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

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

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

try { return true; } finally { return false; }

То же самое с выдачей исключений из блока finally.

Это ДЕЙСТВИТЕЛЬНО плохая практика. См. stackoverflow.com/questions/48088/… для получения дополнительной информации о том, почему это плохо.

John Meagher 16.09.2008 06:47

Согласовано. Возврат в finally {} игнорирует любое исключение, созданное в try {}. Страшный!

neu242 22.10.2008 11:12

Несмотря ни на что, у меня всегда есть ТОЛЬКО ОДИН оператор return в моих методах (который находится в конце), ну, кроме void. Это то, чему меня учили в школе, и я думаю, что это тоже лучшая практика.

dominicbri7 07.07.2011 17:14

@ dominicbri7 Как вы думаете, почему это лучше? И почему должно быть иначе, если функция / метод недействительны?

corsiKa 13.07.2011 00:26

По той же причине я НИКОГДА не использую goto в своих кодах на C++. Я думаю, что множественные возвраты затрудняют чтение и отладку (конечно, в очень простых случаях это не применимо). Я думаю, это просто личное предпочтение, и, в конце концов, вы можете добиться того же, используя любой из методов.

dominicbri7 13.07.2011 15:47

Я обычно использую несколько возвратов в исключительных случаях. Например, если (есть причина не продолжать) return;

iHearGeoff 23.02.2013 03:24

Я интерпретирую блок finally как сокращение для написания одного и того же кода в конце каждого блока catch: `catch (Exc exc) {doSome (); } поймать (AnotherExc exc) {doSome (); }

MC Emperor 29.06.2014 00:06

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

GregT 31.05.2017 18:13

@ MooBob42, должен делать это со встроенным finally

Pacerier 10.05.2020 04:28

Приведен очень интересный пример.

Misho Zhghenti 21.09.2020 00:09

Также возврат в finally отбрасывает любое исключение. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html

Логичный способ подумать об этом:

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

Это просто «логический способ подумать об этом» или действительно так, как предполагается, что блок finally должен работать в соответствии со спецификациями? Здесь была бы очень интересна ссылка на ресурс Sun.

matias 26.05.2010 23:27

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

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

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.");
    }
}

Приведенный выше код выводит:

finally trumps return.
2

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

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

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

Этот пример потрясающий, он добавляет то, что не упоминалось в десятках, наконец, связанных тем. Думаю, вряд ли кто-нибудь из разработчиков об этом узнает.

HopefullyHelpful 08.09.2016 12:11

Что, если бы i был не примитивным, а целочисленным объектом.

Yamcha 09.09.2016 23:50

Мне трудно понять этот случай. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14‌ .17 говорит, что «Оператор возврата с выражением пытается передать управление вызывающей стороне метода или тела лямбда, которое его содержит .... Если оценка выражения завершается нормально, с получением значения V ..» Что я мог догадаться из этого оператора - кажется, что return не оценивает выражение снова после того, как оценивает значение V, поэтому измененный i не влияет на возвращаемое значение, поправьте меня.

meexplorer 15.11.2016 21:21

Но я не смог найти никаких доказательств по этому поводу, где упоминается, что return не оценивает выражение снова.

meexplorer 15.11.2016 21:27

@meexplorer немного опоздал, но это объясняется в JLS 14.20.2. Выполнение try-finally и try-catch-finally - сформулировано немного сложно, 14.17. Заявление о возврате также необходимо прочитать

user85421 17.05.2017 22:18

@ Ямча Все равно было бы так же

Ngupta 05.06.2017 15:35

Я считаю, что если вы добавили оператор return в блок finally, возвращаемое значение было бы 12, а не 2. Пример в основном показывает, что блок finally помещается в стек вызовов точно так же, как вызов метода - что делает локальный копия i, которая возвращается к 2 после выталкивания стека.

Rodney P. Barbati 14.05.2018 20:05

Наконец, всегда выполняется, в этом вся суть, просто потому, что он появляется в коде после возврата, не означает, что это реализовано именно так. Среда выполнения 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 всегда будет вызываться, если вы не вызовете System.exit() (или поток не выйдет из строя).

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

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

Это очень слабый ответ. stackoverflow.com/a/65049/715269

Gangnus 07.05.2019 21:19

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

В дополнение к другим ответам важно указать, что '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();
    }
}

Следует отметить, что средний случай - это именно та причина, по которой наличие оператора return внутри блока finally абсолютно ужасно (оно может скрыть любой Throwable).

Dimitris Andreou 13.05.2010 15:54

Кому не нужен OutOfMemoryError с подавлением? ;)

RecursiveExceptionException 07.02.2019 21:06

Я тестировал его, и он подавляет такую ​​ошибку (ура!). Он также генерирует предупреждение, когда я его компилирую (ура!). И вы можете обойти это, определив возвращаемую переменную, а затем используя return retValпосле блок finally, хотя это, конечно, предполагает, что вы подавили некоторые другие исключения, потому что в противном случае код не имел бы смысла.

Maarten Bodewes 22.12.2019 19:02

Рассмотрим это при нормальном ходе выполнения (то есть без какого-либо исключения): если метод не является `` недействительным '', тогда он всегда явно что-то возвращает, но, наконец, всегда выполняется

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

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

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

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

A try statement with a finally block is executed by first executing the try block. Then there is a choice:

  • If execution of the try block completes normally, [...]
  • If execution of the try block completes abruptly because of a throw of a value V, [...]
  • If execution of the try block completes abruptly for any other reason R, then the finally block is executed. Then there is a choice:
    • If the finally block completes normally, then the try statement completes abruptly for reason R.
    • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

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

JLS 14.17 Заявление о возврате

ReturnStatement:
     return Expression(opt) ;

A return statement with no Expressionattempts to transfer control to the invoker of the method or constructor that contains it.

A return statement with an Expressionattempts to transfer control to the invoker of the method that contains it; the value of the Expression becomes the value of the method invocation.

The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements within the method or constructor whose try blocks contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

Если выбрано исключение, функция finally выполняется. Если исключение не сгенерировано, функция finally выполняется. Если исключение обнаружено, наконец запускается. Если исключение не обнаружено, функция finally выполняется.

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

Попробуйте этот код, вы поймете код в блоке finally выполняется после оператора возврата.

public class TestTryCatchFinally {
    static int x = 0;

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

    public static int f1(){
        try{
            x = 1;
            return x;
        }finally{
            x = 2;
        }
    }

    public static int f2(){
        return x;
    }
}

Нет, не всегда одним исключением является // 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");
        }
    }
}

И это одна из причин, по которой вам действительно никогда не следует вызывать System.exit () ...

Franz D. 10.09.2018 21:34

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

Вот разработка Ответ Кевина. Важно знать, что возвращаемое выражение оценивается до 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

Важно знать.

Aminadav Glickshtein 06.07.2018 15:06

Полезно знать, и это тоже имеет смысл. Похоже, что на самом деле возвращаемое значение return - это то, что происходит после finally. Вычисление возвращаемого значения (здесь printX()) все еще предшествует этому.

Albert 26.06.2019 13:58

Неа. Приведенный выше код должен заменить System.out.println("finally trumps return... sort of"); на System.out.print("finally trumps return in try"); return 42;.

Pacerier 10.05.2020 04:21

Да, это будет. Только в том случае, если этого не произойдет, это выход из JVM или сбой

Блок finally всегда выполняется независимо от того, обрабатывается ли исключение или нет. Если какое-либо исключение произошло до блока try, блок finally не будет выполняться.

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

If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

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

finally будет работать, и это точно.

finally не будет выполняться в следующих случаях:

Случай 1 :

Когда вы выполняете System.exit().

случай 2:

Когда ваша JVM / Thread аварийно завершает работу.

случай 3:

Когда ваше выполнение остановлено вручную.

Я был очень смущен всеми ответами, представленными на разных форумах, и решил наконец написать код и посмотреть. Выход:

finally будет выполняться, даже если в блоке try и 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");
}

Выход

try

Print me FINALLY

  1. Если return заменяется на System.exit(0) в блоке try and catch в приведенном выше коде и перед ним возникает исключение по любой причине.

НЕ ВСЕГДА

Спецификация языка 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 не гарантирует, что ПЛАВНИК выполняется после КОД. JLS гарантирует, что если выполняются КОД и СЛЕДУЮЩИЙ, то ПЛАВНИК всегда будет выполняться после КОД и перед СЛЕДУЮЩИЙ.

Почему JLS не гарантирует, что блок finally всегда выполняется после блока try? Потому что это невозможно. Маловероятно, но возможно, что JVM будет прервана (уничтожение, сбой, отключение питания) сразу после завершения блока try, но до выполнения блока finally. JLS ничего не может сделать, чтобы этого избежать.

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

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

Я пробовал это, Он однопоточный.

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");
    }
}

mainThread навсегда останется в состоянии wait, следовательно, finally никогда не будет называться,

поэтому вывод консоли не будет printString: после 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

См. Принятый ответ. Это просто крайний случай «бесконечного цикла».

Stephen C 20.08.2017 04:27

finally также может быть преждевременно завершен, если Exception является thrown внутри вложенный блок finally. Компилятор предупредит вас о том, что блок finally не завершается нормально, или выдаст ошибку о недоступности кода. Ошибка для недоступного кода будет отображаться только в том случае, если throw не находится за условным оператором или внутри цикла.

try{
}finally{
   try{
   }finally{
      //if (someCondition) --> no error because of unreachable code
      throw new RunTimeException();
   }
   int a = 5;//unreachable code
}

Просто замечу, что это совершенно нормальное поведение. Любой / весь код преждевременно завершается, если возникает исключение. Это ничем не отличается от исключения, созданного где-либо еще. В спецификации нет специального правила, которое гласит, что «исключения не могут использоваться внутри блока finally». Из-за этого я сначала даже неправильно понял ваш ответ и чуть не дал -1, пока не перечитал его пару раз.

Loduwijk 03.01.2018 18:05

Ну, а чем отдача отличается от броска? Если вы вернетесь, тогда блок должен вернуться, но в блоке finally возврат будет отложен, и есть какое-то странное исключение для возврата, поэтому неясно, обрабатывает ли finally throw обычным образом. Это основная причина, по которой я написал это тогда. Также добавление блока try или catch не будет препятствовать выполнению блока finally, поэтому, конечно же. можно ожидать, что когда вы добавляете блок finally, он все равно выполняется.

HopefullyHelpful 03.01.2018 23:56

"Чем возврат отличается от броска?" В некотором смысле, ни один из которых здесь не имеет отношения. В блоке finally возврат не откладывается, или вы имели в виду, что возврат в блоке try откладывается до тех пор, пока не будет выполнен блок finally (в этом случае да, это правда)? В любом случае то же самое относится и к выброшенным исключениям. Независимо от того, что происходит в try, когда достигается конец try, управление переходит к finally, затем все те же правила применяются в finally (с любой попыткой / уловом внутри, наконец, работает, как обычно).

Loduwijk 04.01.2018 03:24

Я не говорю, что ваш ответ неправильный, бесполезный или что-то в этом роде. Технически это правильно. Я просто заметил, что описанное вами поведение не является нарушением правила try / finally, которое просто гласит: «Независимо от того, что происходит в try/catch, как только они будут выполнены, поток управления буду перейдет к блоку finally». Гарантия try / finally, по сути, не более чем , не меньше этого. Внутри любого блока, будь то try / catch / finally или иначе, вы можете вкладывать в него другие блоки try / catch / finally, как вы предлагаете. Действительно, некоторые люди об этом не подозревают.

Loduwijk 04.01.2018 03:29

То же самое со следующим кодом:

static int f() {
    while (true) {
        try {
            return 1;
        } finally {
            break;
        }
    }
    return 2;
}

f вернет 2!

Дублирующий ответ

Stephen C 20.08.2017 04:25

Answer is simple YES.

ВХОД:

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

Ответ прост: НЕТ.

Christophe Roussy 14.12.2017 11:25

@ChristopheRoussy Как? Вы можете объяснить, пожалуйста?

Meet Vora 14.12.2017 12:24

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

Christophe Roussy 14.12.2017 12:58

тогда в каком случае он не будет выполнен?

Meet Vora 15.12.2017 13:41

Во всех случаях, упомянутых в других ответах, см. Принятый ответ с 1000+ голосами.

Christophe Roussy 18.12.2017 12:37

Да, он всегда будет вызываться, но в одной ситуации он не вызывается, когда вы используете System.exit ()

try{
//risky code
}catch(Exception e){
//exception handling code
}
finally(){
//It always execute but before this block if there is any statement like System.exit(0); then this block not execute.
}

Дублирующий ответ

Stephen C 20.08.2017 04:25

Не только «дублирующий», но и противоречивый.

user207421 08.08.2019 14:02
  1. Наконец, блок всегда выполняется. Если и пока Здесь существует оператор System.exit () (первый оператор в блоке finally).
  2. Если system.exit () является первым оператором, то блок finally не будет выполняться, и управление будет передано из блока finally. Всякий раз, когда оператор System.exit () попадает в блок finally, до тех пор, пока этот блок не будет выполнен, и когда появится System.exit (), тогда управляющая сила полностью выйдет из блока finally.

На этот вопрос ответили много раз, так какую новую информацию добавляет ваш ответ?

Tom 20.05.2017 15:19

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

  1. Вызывая фатальную ошибку, приводящую к прерыванию процесса.

  2. Завершение программы из-за повреждения памяти.

  3. Вызывая System.exit ()

  4. Если программа переходит в бесконечный цикл.

Блок finally не будет вызываться после возврата в нескольких уникальных сценариях: если сначала вызывается System.exit () или если JVM дает сбой.

Позвольте мне попытаться ответить на ваш вопрос как можно проще.

Правило 1: блок finally всегда выполняется (Хотя есть исключения из этого. Но давайте пока останемся на этом.)

Правило 2: операторы в блоке finally выполняются, когда управление выходит из блока try или catch. Передача управления может происходить в результате нормального выполнения, выполнения оператора break, continue, goto или return или распространения исключения.

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

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

Вы можете сослаться на лучшее объяснение здесь: http://msdn.microsoft.com/en-us/ .... концепция в основном одинакова для всех языков высокого уровня.

Да, потому что нет контрольного заявления может предотвратить выполнение finally.

Вот справочный пример, в котором будут выполняться все блоки кода:

| x | Current result | Code 
|---|----------------|------ - - -
|   |                |     
|   |                | public static int finallyTest() {
| 3 |                |     int x = 3;
|   |                |     try {
|   |                |        try {
| 4 |                |             x++;
| 4 | return 4       |             return x;
|   |                |         } finally {
| 3 |                |             x--;
| 3 | throw          |             throw new RuntimeException("Ahh!");
|   |                |         }
|   |                |     } catch (RuntimeException e) {
| 4 | return 4       |         return ++x;
|   |                |     } finally {
| 3 |                |         x--;
|   |                |     }
|   |                | }
|   |                |
|---|----------------|------ - - -
|   | Result: 4      |

В варианте ниже return x; будет пропущен. Результат по-прежнему 4:

public static int finallyTest() {
    int x = 3;
    try {
        try {
            x++;
            if (true) throw new RuntimeException("Ahh!");
            return x; // skipped
        } finally {
            x--;
        }
    } catch (RuntimeException e) {
        return ++x;
    } finally {
        x--;
    }
}

Ссылки, конечно же, отслеживают их статус. Этот пример возвращает ссылку с value = 4:

static class IntRef { public int value; }
public static IntRef finallyTest() {
    IntRef x = new IntRef();
    x.value = 3;
    try {
        return x;
    } finally {
        x.value++; // will be tracked even after return
    }
}

Да написано здесь

If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

Итак, когда вы говорите «да», вы имеете в виду «нет»?

user207421 08.08.2019 14:04

Добавление к Ответ @ 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 

Начиная с Java 1.8.162, это не результат.

sam 30.04.2018 11:28

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

try {
     //code statements
     //exception thrown here
     //lines not reached if exception thrown
} catch (Exception e) {
    //lines reached only when exception is thrown
} finally {
    // always executed when the try block is exited
    //independent of an exception thrown or not
}

Блок finally предотвращает выполнение ...

  • Когда вы позвонили в System.exit(0);
  • Если JVM завершает работу.
  • Ошибки в JVM

Блок finally выполняется всегда, даже если вы поместите оператор return в блок try. Блок finally будет выполнен перед оператором return.

Рассмотрим следующую программу:

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)?

user7294900 05.09.2018 09:38

sb.setLength (0) просто очистит данные в StringBuffer. Итак, sb = null отсоединит объект от ссылки.

sam 05.09.2018 20:16

Разве «xyz» не следует печатать дважды на выходе? Поскольку функция вызывалась дважды, почему «finally» было только один раз?

fresko 13.02.2019 13:22

@fresko: finallly определенно вызывается дважды, но когда дело доходит до вывода в консоли, он будет отображаться только один раз из-за оператора return. Вы можете запустить программу и увидеть результат, а после этого добавить еще один оператор System.out.println(sb.toString());.

sam 13.02.2019 18:21

Это не очень хорошая практика. Последний блок с sb = null; просто добавляет ненужный код. Я понимаю, что вы имеете в виду, что блок finally - хорошее место для освобождения ресурсов, таких как соединение с базой данных или что-то в этом роде, но имейте в виду, что ваш пример может запутать новичков.

Roc Boronat 16.02.2019 02:58

@Samim Спасибо, я добавил строки System.out.println("---AGAIN2---");System.out.println(sb);, и теперь это более понятно. Как бы то ни было, вывод противоречил вашему тезису: p Я также добавил к вашему ответу, но редактирование должно быть принято модератором или кем-то в этом роде. В противном случае вы можете добавить их

fresko 19.02.2019 16:08

Наконец, всегда вызывается в конце

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

Наконец, обычно используется при очистке, например, если вы используете сканер в java, вам, вероятно, следует закрыть сканер, поскольку это приводит к другим проблемам, таким как невозможность открыть какой-либо файл.

Вот некоторые условия, которые могут обойти блок finally:

  1. Если JVM завершает работу во время выполнения кода try или catch, то блок finally может не выполняться. Подробнее о солнце учебник
  2. Нормальное завершение работы - это происходит либо при выходе из последнего недемонического потока, либо при Runtime.exit () (какой-то хороший блог). Когда поток завершается, JVM выполняет инвентаризацию запущенных потоков, и если единственные оставшиеся потоки являются потоками демона, она инициирует упорядоченное завершение работы. Когда JVM останавливается, все оставшиеся потоки демона прекращаются, в конце концов, блоки не выполняются, стеки не разматываются, JVM просто завершает работу. Демонические потоки следует использовать с осторожностью, от некоторых операций обработки можно безопасно отказаться в любое время без очистки. В частности, опасно использовать потоки демонов для задач, которые могут выполнять какой-либо ввод-вывод. Потоки демона лучше всего сохранять для «служебных» задач, таких как фоновый поток, который периодически удаляет просроченные записи из кеша в памяти (источник)

Пример выхода последнего недемонического потока:

public class TestDaemon {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("Is alive");
                    Thread.sleep(10);
                    // throw new RuntimeException();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                System.out.println("This will never be executed.");
            }
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread daemon = new Thread(runnable);
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(100);
        // daemon.stop();
        System.out.println("Last non-daemon thread exits.");
    }
}

Выход:

Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive

finally выполняется всегда и перед возвратом (вычисленного) значения x.

System.out.println(foo());

....

int foo(){
    int x = 2;
    try{
        return x++;
    } finally{
        System.out.println(x);
    }
}

Выход:

3
2

Хороший наглядный пример, +1.

President James K. Polk 04.02.2020 22:37

пример попытки с ресурсом

static class IamAutoCloseable implements AutoCloseable {
    private final String name;
    IamAutoCloseable(String name) {
        this.name = name;
    }
    public void close() {
        System.out.println(name);
    }
}

@Test
public void withResourceFinally() {
    try (IamAutoCloseable closeable1 = new IamAutoCloseable("closeable1");
         IamAutoCloseable closeable2 = new IamAutoCloseable("closeable2")) {
        System.out.println("try");
    } finally {
        System.out.println("finally");
    }
}

Тестовый результат:

try
closeable2
closeable1
finally

Другие вопросы по теме