34

在 Javatry{} ... catch{} ... finally{}块中,finally{}无论 try/catch 中发生什么,通常都认为“保证”可以运行其中的代码。但是,我知道至少有两种情况下它不会执行:

  • 如果System.exit(0)被调用;或者,
  • 如果一直抛出异常到 JVM 并且发生默认行为(即printStackTrace()退出)

是否有任何其他程序行为会阻止finally{}块中的代码执行?代码将在什么特定条件下执行或不执行?

编辑:正如 NullUserException 指出的,第二种情况实际上是不正确的。我认为这是因为标准错误中的文本在标准输出之后打印,防止在不向上滚动的情况下看到文本。:) 道歉。

4

9 回答 9

37

如果你调用System.exit()程序立即退出而不finally被调用。

JVM 崩溃,例如分段错误,也将阻止最终被调用。即 JVM 在此时立即停止并生成崩溃报告。

无限循环也会阻止 finally 被调用。

finally 块总是在抛出 Throwable 时调用。即使您调用 Thread.stop() 会触发ThreadDeath在目标线程中抛出的 a。这可以被捕获(它是一个Error)并且 finally 块将被调用。


public static void main(String[] args) {
    testOutOfMemoryError();
    testThreadInterrupted();
    testThreadStop();
    testStackOverflow();
}

private static void testThreadStop() {
    try {
        try {
            final Thread thread = Thread.currentThread();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    thread.stop();
                }
            }).start();
            while(true)
                Thread.sleep(1000);
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testThreadInterrupted() {
    try {
        try {
            final Thread thread = Thread.currentThread();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    thread.interrupt();
                }
            }).start();
            while(true)
                Thread.sleep(1000);
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testOutOfMemoryError() {
    try {
        try {
            List<byte[]> bytes = new ArrayList<byte[]>();
            while(true)
                bytes.add(new byte[8*1024*1024]);
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testStackOverflow() {
    try {
        try {
            testStackOverflow0();
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testStackOverflow0() {
    testStackOverflow0();
}

印刷

finally called after java.lang.OutOfMemoryError: Java heap space
finally called after java.lang.InterruptedException: sleep interrupted
finally called after java.lang.ThreadDeath
finally called after java.lang.StackOverflowError

注意:在每种情况下,线程都保持运行,即使在 SO、OOME、Interrupted 和 Thread.stop() 之后!

于 2012-09-14T19:22:40.777 回答
9

块中的无限循环try

损坏的内存?程序不再像写的那样运行?我实际上已经在 DOS 机器上调试过一次。

于 2012-09-14T19:18:28.800 回答
2

在 try 块中的不同语句中测试finally块。

 public static void main(String [] args){

    try{
        System.out.println("Before Statement");
        /*** Statement ***/
        System.out.println("After Statement");
    }
    catch(Exception e){
    }
    finally{
        System.out.println("Finally is Executed");
    }

执行 finally 块的语句如下:

  1. Thread.currentThread().interrupted();
  2. Thread.currentThread().destroy();
  3. Thread.currentThread().stop();
  4. Thread.sleep(10);
  5. Thread.currentThread().interrupt();
  6. Runtime.getRuntime().addShutdownHook(Thread.currentThread());
  7. 如果有任何异常发生。
  8. 如果没有例外。

不执行 finally 块的语句如下:

  1. Thread.currentThread().suspend();
  2. System.exit(0);
  3. JVM 崩溃了。
  4. CPU 芯片的电源关闭。
  5. 操作系统杀死 JVM 进程。
  6. Runtime.getRuntime().exit(0);
  7. Runtime.getRuntime().halt(0);
于 2017-10-01T10:06:19.587 回答
1

当 finally 本身抛出异常(或导致错误)时,有可能部分执行

于 2012-09-14T19:20:16.487 回答
1

一个可能是“A finally 是守护线程的一部分,它可能不会被执行”。

于 2012-09-14T19:41:15.907 回答
1

最终不会被调用的唯一时间是:

如果电源关闭

  1. 如果你调用 System.exit()
  2. 如果 JVM 先崩溃
  3. 如果 try 块中有无限循环
  4. 如果电源关闭
于 2016-12-01T06:33:49.797 回答
0

我认为当JVM由于任何原因突然退出时,这可能是导致控件不会进入finally块并且永远不会执行的原因。

于 2013-05-02T04:16:23.960 回答
0

你可以让它成为守护线程的一部分。您可以使用setDaemon(boolean status)用于将当前线程标记为守护线程或用户线程的方法,并在需要时退出JVM。这将使您能够在finally{}执行之前退出 JVM。

于 2013-12-21T18:18:07.467 回答
0

finally 块永远不会执行的另一个可能实例是由于在进入 try 块之前方法返回的设计,就像我不时看到的一些非常糟糕的代码的情况一样:

public ObjectOfSomeType getMeAnObjectOfSomeType() throws SomeHorrendousException {
    if (checkSomeObjectState()) {
        return new ObjectOfSomeType();
    }

    try {
        // yada yada yada...
    } catch (SomeHorrendousException shexc) {
        // wow, do something about this horrendous exception...
    } finally {
        // do some really important cleanup and state invalidation stuff...
    }

我知道你们中没有人会这样做,所以我犹豫是否将其添加为可能的场景,但我想,嗯,今天是星期五,见鬼;)

于 2017-11-04T01:11:18.467 回答