Всегда ли выполняется блок finally в Java? - PullRequest
2172 голосов
/ 15 сентября 2008

Учитывая этот код, могу ли я быть абсолютно уверенным , что блок 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");
}

Ответы [ 46 ]

7 голосов
/ 15 января 2014

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

7 голосов
/ 16 августа 2013

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

7 голосов
/ 30 апреля 2018

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

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.
    }
}
6 голосов
/ 15 сентября 2008

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

6 голосов
/ 20 марта 2016

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

class Test {
    public static void main(String args[]) throws Exception {
       Object obj = new Object();
       try {
            synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
           }
       } catch (Exception e) {
       } finally {
           System.out.println("finally");
       }
   }
}

Основной поток будет находиться в состоянии ожидания навсегда, поэтому, наконец, никогда не будет вызван,

, поэтому вывод консоли не будет печатать строку: after wait() или finally

Согласовано с @Stephen C, приведенный выше пример относится к третьему упоминанию дела здесь :

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

// import java.util.concurrent.Semaphore;
class Test {
    public static void main(String[] args) {
        try {
            // Thread.sleep(Long.MAX_VALUE);
            // Thread.currentThread().join();
            // new Semaphore(0).acquire();
            // while (true){}
            System.out.println("after sleep join semaphore exit infinite while loop");
        } catch (Exception e) {
        } finally {
            System.out.println("finally");
        }
    }
}

Случай 2: если JVM аварийно завершает работу

import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Test {
    public static void main(String args[]) {
        try {
            unsafeMethod();
//            Runtime.getRuntime().halt(123);
            System.out.println("After Jvm Crash!");
        } catch (Exception e) {
        } finally {
            System.out.println("finally");
        }
    }

    private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        unsafe.putAddress(0, 0);
    }
}

Ссылка: Как вы разбили JVM?

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

class Test {
    public static void main(String args[]) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    printThreads("Daemon Thread printing");
                    // just to ensure this thread will live longer than main thread
                    Thread.sleep(10000);
                } catch (Exception e) {
                } finally {
                    System.out.println("finally");
                }
            }
        };
        Thread daemonThread = new Thread(runnable);
        daemonThread.setDaemon(Boolean.TRUE);
        daemonThread.setName("My Daemon Thread");
        daemonThread.start();
        printThreads("main Thread Printing");
    }

    private static synchronized void printThreads(String str) {
        System.out.println(str);
        int threadCount = 0;
        Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
        for (Thread t : threadSet) {
            if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
                System.out.println("Thread :" + t + ":" + "state:" + t.getState());
                ++threadCount;
            }
        }
        System.out.println("Thread count started by Main thread:" + threadCount);
        System.out.println("-------------------------------------------------");
    }
}

output: не выводится «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 голосов
/ 16 апреля 2018

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 голосов
/ 16 декабря 2017

Да, потому что без управляющего оператора может помешать выполнению 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 голосов
/ 08 июня 2017

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

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

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

  3. Вызов System.exit ()

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

6 голосов
/ 18 декабря 2014

finally выполнится, и это точно.

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

дело 1:

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

дело 2:

Когда происходит сбой вашей JVM / Thread.

дело 3:

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

6 голосов
/ 10 апреля 2018

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