提问



考虑到这段代码,我可以完全确定 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.");
}

最佳参考


是的,在执行try或catch代码块之后将调用finally


唯一finally不被称为的是:



  1. 如果您调用System.exit();

  2. 如果JVM首先崩溃;

  3. 如果JVM在trycatch块中达到无限循环(或其他一些不可中断的,非终止语句);

  4. 如果操作系统强行终止JVM进程;例如UNIX上的kill -9。

  5. 如果主机系统死亡;例如电源故障,硬件错误,操作系统恐慌等。

  6. 如果最终块将由守护程序线程执行,并且所有其他非守护程序线程在最终被调用之前退出。


其它参考1


示例代码:


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

其它参考2


此外,虽然这是不好的做法,如果在finally块中有一个return语句,它将胜过常规块的任何其他返回。也就是说,以下块将返回false:


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


从finally块中抛出异常也是一样的。

其它参考3


这是Java语言规范中的官方文字。


14.20.2。执行try-finally和try-catch-finally [71]



  通过首先执行try块来执行带finally块的try语句。然后有一个选择:

  
  

      
  • 如果try块的执行正常完成,[[...]]

  •   
  • 如果try块的执行由于值 V 的throw而突然完成,[[...]]

  •   
  • 如果try块的执行由于任何其他原因而突然完成 R ,则执行finally块。然后有一个选择:
      
      

        
    • 如果finally块正常完成,则try语句突然完成,原因是 R 。

    •   
    • 如果finally块因 S 原因突然完成,则try语句突然完成,原因是 S (和原因 R 被丢弃)。

    •   

  •   



return的规范实际上明确地说明了这一点:


JLS 14.17退货声明[72]



ReturnStatement:
     return Expression(opt) ;

  
  没有Expression return语句尝试将控制权转移给包含它的方法或构造函数的调用者。

  
  带Expression return语句尝试将控制权转移给包含它的方法的调用者; Expression的值成为方法调用的值。

  
  前面的描述说 尝试转移控制而不仅仅是转移控制,因为如果内部有任何try语句try块包含return语句的方法或构造函数,那些try语句的任何finally子句将按顺序执行,最内层到最外层,在控制之前转移到方法或构造函数的调用者。突然完成finally条款可能会破坏由return语句启动的控制权转移。


其它参考4


除了其他响应之外,重要的是要指出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();
    }
}

其它参考5


我尝试了上面的例子,略有修改 -


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,因为此return语句不会再次执行。


如果您将在Eclipse中调试此代码,那么您将感觉在执行finally finally之后,try块的return语句再次执行。但是这事实并非如此。它只返回值2。

其它参考6


这是凯文回答的详细说明。重要的是要知道要返回的表达式在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

其它参考7


这就是最终块的整体想法。它允许您确保进行清理,否则可能会因为您返回而被忽略,当然。


最后在try块中调用,无论发生什么(除非你调用System.exit(int)或Java虚拟机因其他原因而踢出)。

其它参考8


考虑这一点的合理方式是:



  1. 放置在finally块中的代码必须在try块
  2. 中执行发生的任何
  3. 因此,如果try块中的代码尝试返回值或抛出异常,则项目将置于架子上,直到finally块可以执行

  4. 因为finally块中的代码(根据定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,任何留在货架上的东西都会被丢弃。

  5. 唯一的例外是VM在try块期间完全关闭,例如通过System.exit


其它参考9


最后的回报也会抛弃任何异常。 http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html[74]

其它参考10


除非程序异常终止(如调用System.exit(0)..),否则最后总是执行。所以,你的sysout会打印出来

其它参考11


不,并非总是一个例外情况//
System.exit(0);
在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("No error");
        }
    }
}

其它参考12


除非由于JVM崩溃或调用System.exit(0)导致程序异常终止,否则始终执行finally块。


最重要的是,finally块中返回的任何值都将覆盖执行finally块之前返回的值,因此在使用try finally时要小心检查所有出口点。

其它参考13


终于总是运行这一点,只是因为它在返回之后出现在代码中并不意味着它是如何实现的。退出try块时,Java运行时负责运行此代码。


例如,如果您有以下内容:


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


运行时将生成如下内容:


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


如果抛出未捕获的异常,finally块将运行,异常将继续传播。

其它参考14


因为除非你调用System.exit()(或线程崩溃),否则将始终调用finally块。

其它参考15


简而言之,在官方Java文档(点击这里)中写道 - [75]



  如果在执行try或catch代码时JVM退出,那么
  finally块可能无法执行。同样,如果线程正在执行
  try或catch代码被中断或终止,finally块可以
  即使整个应用程序仍在继续,也不会执行。


其它参考16


这是因为您将i的值指定为12,但未将i的值返回给函数。正确的代码如下:


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

其它参考17


是的,它会被调用。这就是拥有finally关键字的重点。如果跳出try/catch块只能跳过finally块,那么将System.out.println放在try/catch之外是一样的。

其它参考18


是的,它会。无论你的try或catch块发生了什么,除非调用System.exit()或JVM崩溃。如果块中有任何return语句,则最终将在该return语句之前执行。

其它参考19


是的,它会。
只有JVM退出或崩溃才会出现这种情况

其它参考20


是的,最后块总是执行。大多数开发人员使用此块关闭数据库连接,结果集对象,语句对象以及使用java hibernate来回滚事务。

其它参考21


这在任何语言中都是真的......最终总是会在return语句之前执行,无论返回的是在方法体中的哪个位置。如果不是这样的话,finally块就没有多大意义。

其它参考22


因为无论你有什么案例,总是会被调用。你没有异常,它仍然被调用,捕获异常,它仍然被调用

其它参考23


在正常的执行过程中考虑这一点(即没有抛出任何异常):如果方法不是void,那么它总是显式返回一些东西,但最后总是被执行

其它参考24


如果抛出异常,最后运行。如果没有抛出异常,最后运行。如果捕获到异常,则最终运行。如果未捕获异常,则最终运行。


只有当JVM退出时它才会运行。

其它参考25


最后块总是执行是否处理异常。如果在try块之前发生任何异常,则finally块将不会执行。

其它参考26


考虑这一点的合理方式是:


放置在finally块中的代码必须在try块内发生。


因此,如果try块中的代码尝试返回值或抛出异常,则将项放在架子上,直到finally块可以执行
因为finally块中的代码(按照定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,货架上留下的任何东西都将被丢弃。


唯一的例外是VM在try块期间完全关闭,例如通过'System.exit'



永远不会抛出finally块中的任何异常


try {
  someMethod();  //Throws exceptionOne
} finally {
  cleanUp();    //If finally also threw any exception the exceptionOne will be lost forever
}


这很好,只要cleanUp()永远不会抛出任何异常。在上面的示例中,如果someMethod()抛出异常,并且在finally块中,cleanUp()也会抛出异常,第二个异常将来自方法,并且原始的第一个异常(正确的原因)将永远丢失。如果您在finally块中调用的代码可能会抛出异常,请确保您要么处理它,要么记录它。永远不要让它从最后一块出来。


实际上退出程序(通过调用System.exit()或导致导致进程中止的致命错误:有时在Windows中非正式地称为热点或Dr Watson)将阻止您的finally块被阻止执行!


没有什么可以阻止我们嵌套try/catch/finally块(例如,将try/finally块放在try/catch块中,反之亦然),这并不是一件不寻常的事情。

其它参考27



  答案很简单



INPUT:


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

其它参考28


考虑以下程序:


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

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}


从Java 1.8.162开始,上面的代码块给出了以下输出:


-abc-
---AGAIN---
-abc-xyz-abc-


这意味着使用finally释放对象是一个很好的做法,如下面的代码:


private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null;
    }
}

其它参考29


除了最后替换try块中的返回的返回点之外,异常也是如此。抛出异常的finally块将替换try块内抛出的返回或异常。