it-swarm-ru.tech

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

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

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

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

Пример кода:

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
507
Kevin

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

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

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

363
MooBob42

Вот официальные слова из спецификации языка [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.

249
polygenelubricants

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

Я попробовал приведенный выше пример с небольшой модификацией

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, поскольку этот оператор return больше не выполняется.

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

109
vibhash

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

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

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

52
Chris Cooper

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

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

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

18
shyam

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

18
James A. N. Stauffer

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

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

17
user9189

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

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

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

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

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

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

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

12
Motti

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

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

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

9
Jay Riggs

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

ВХОД:

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
9
Meet

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

8
Mendelt

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

8
Gautam Viradiya

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

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

8
bikz05

Да, это будет. Единственный случай, когда это не будет, JVM выходит или вылетает

7
abhig

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

7
Karthikeyan

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

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.
    }
}
7
Samim

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

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, приведенный выше пример является одним из упоминаний о 3-м случае здесь :

Добавим еще несколько таких возможностей бесконечного цикла в следующем коде:

// 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
6
dkb

Да, потому что без управляющего оператора может помешать выполнению 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
    }
}
6
Dávid Horváth

finally будет выполнен, и это точно.

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

случай 1 :

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

случай 2:

Когда ваша JVM/Thread падает.

случай 3:

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

6
Utkash Bhatt

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
6
Poorna Senani Gamage

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

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

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

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

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

6
Vikas Suryawanshi

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

6
Scott Dorman

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

Да, это написано здесь

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

5
Danail Tsvetanov

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

5
Alex Miller

НЕ ВСЕГДА

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

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

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

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

5
Jose Antonio Dura Olmos

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

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

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

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

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

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

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

5
Sandip Solanki
  1. Наконец, Блок всегда исполняется. Если и до тех пор, пока System.exit () оператор не существует (первый оператор в блоке finally).
  2. Если system.exit () является первым оператором, то блок finally не будет выполнен, и управление выйдет из блока finally. Всякий раз, когда оператор System.exit () попадает в блок finally, пока этот оператор не завершит выполнение блока, а когда появится System.exit (), управляющая сила полностью выйдет из блока finally.
5
Avinash Pande

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

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

f вернет 2!

4
dibo

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

4
vodkhang

Да, он всегда будет вызываться, но в одной ситуации он не будет вызываться при использовании 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.
}
4
Akash Manngroliya

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

4
Rohit Chugh

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

Только когда он не запускается, происходит выход из JVM.

4
Bhushan

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

4
Gala101

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

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;
    }
}
4
eric2323223

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

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

Результат

пытаться

Напиши мне, наконец,

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

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

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

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

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

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

2
Rubin Luitel

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

2
Sabrina