46 题: finally块总是在Java中执行吗?

在...创建的问题 Tue, Apr 23, 2019 12:00 AM

考虑到这段代码,我可以完全确定 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");
}
    
2182
  1. 如果没有,则应将关键字命名为probably
    2010-05-13 06:18:05Z
  2. 2013-09-23 00:52:04Z
  3. 2014-12-14 03:08:59Z
  4. @ BinoyBabu, finalizer != finally; 终结者 == finalize()方法。
    2016-07-12 16:11:35Z
  5. @ LordFarquaad ;-)这确实始终得到保证。
    2017-11-14 21:10:51Z
  6. 醇>
    30答案                              30 跨度>                         

    是的,finally将在执行trycatch代码块后被调用。

    finally将不会被调用的唯一时间是:

    1. 如果您调用System.exit()
    2. 如果JVM首先崩溃
    3. 如果JVM在trycatch块中达到无限循环(或其他一些不可中断的非终止语句)
    4. 如果操作系统强行终止JVM进程;例如,UNIX上的kill -9 <pid>
    5. 如果主机系统死亡;例如,电源故障,硬件错误,操作系统恐慌等等
    6. 如果finally块将由守护程序线程执行,而所有其他非守护程序线程在调用finally之前退出
    2443
    2019-04-23 14:01:34Z
    1. 实际上,thread.stop()并不一定会阻止执行finally块。
      2011-03-30 21:12:19Z
    2. 我们怎么说finally块在之后被称为 try块,并且之前控制传递给以下陈述。这与涉及无限循环的try块一致,因此finally块实际上从未被调用过。
      2011-09-16 11:24:48Z
    3. 当我们使用嵌套 try-catch-finally
      时,还有另一种情况
      2014-03-22 20:39:34Z
    4. 另外,如果守护程序线程抛出异常,则不会调用块。
      2014-09-11 12:07:46Z
    5. @ BinoyBabu - 那是关于终结者,而不是最终阻止
      2017-01-05 10:17:01Z
    6. 醇>

    示例代码:

     
    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
    
        
    509
    2017-12-05 17:20:06Z
    1. 仅供参考:在C#中,除了替换之外,行为是相同的不允许使用finally-49910中的语句(编译器错误)。
      2013-10-31 08:08:33Z
    2. 这是一个需要注意的重要细节: stackoverflow.com/a /2684342分之20363941
      2013-12-03 23:37:57Z
    3. 您甚至可以在finally块本身中添加一个return语句,然后覆盖之前的返回值。这也神奇地丢弃了未处理的异常。此时,您应该考虑重构代码。
      2015-04-15 17:25:33Z
    4. 这并没有真正证明最终胜出归来。从调用者代码打印返回值。似乎没有多大证据。
      2016-06-21 04:20:19Z
    5. 抱歉,这是演示而不是证据。如果您可以证明此示例在所有Java平台上始终以这种方式运行,那么这只是一个证明,并且类似示例也始终以这种方式运行。
      2017-08-20 01:24:21Z
    6. 醇>

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

     return 2;

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

        
    364
    2008-09-15 18:19:53Z
    1. 这是一个非常糟糕的做法。请参阅 stackoverflow.com/questions/48088 /... 了解更多关于它为什么不好的信息。
      2008-09-16 02:47:56Z
    2. 同意。 finally {}内的返回忽略try {}中抛出的任何异常。吓人!
      2008-10-22 07:12:20Z
    3. @ dominicbri7为什么你认为这是一个更好的做法?当函数/方法无效时,为什么它会有所不同?
      2011-07-12 20:26:36Z
    4. 出于同样的原因,我从不在我的C ++代码中使用goto。我认为多次返回会使其难以阅读并且更难以调试(当然,在非常简单的情况下,它不适用)。我想这只是个人的偏好,最后你可以使用任何一种方法
      来实现同样的目的
      2011-07-13 11:47:11Z
    5. 当某种特殊情况发生时,我倾向于使用一些返回值。喜欢if(有理由不继续)返回;
      2013-02-22 23:24:00Z
    6. 醇>

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

    14.20.2。执行try-finally和try-catch-finally

      

    首先执行

    try { return true; } finally { return false; }
    
    块,执行带有try块的finally语句。然后有一个选择:

         
    • 如果try块的执行正常完成,[...]
    •   
    • 如果由于try的值 V 突然完成try块的执行,[...]
    •   
    • 如果由于任何其他原因 R 突然执行throw块,则执行try块。然后有一个选择:      
      • 如果finally块正常完成,那么finally语句因 R 原因而突然完成。
      •   
      • 如果try块突然因 S 而突然完成,则finally语句突然完成,原因是 S 和原因 R 被丢弃)。
      •   
    •   

    try的规范实际上明确指出:

    JLS 14.17返回声明

     return      

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

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

         

    前面的说明说“ 尝试转移控制”而不仅仅是“转移控制”,因为如果其中有任何Expression个语句如果方法或构造函数的try块包含try语句,则在将控制权转移给方法或构造函数的调用者之前,将按顺序执行这些return语句中的任何finally子句,最内层到最外层。突然完成try条款可能会破坏由finally声明发起的控制权转移。

        
    249
    2016-06-07 19:51:36Z

    除了其他响应之外,重要的是要指出'finally'有权通过try..catch块覆盖任何异常/返回值。例如,以下代码返回12:

     return

    同样,以下方法不会抛出异常:

     
    public static int getMonthsInYear() {
        try {
            return 10;
        }
        finally {
            return 12;
        }
    }
    

    虽然以下方法确实抛出它:

     
    public static int getMonthsInYear() {
        try {
            throw new RuntimeException();
        }
        finally {
            return 12;
        }
    }
    
        
    148
    2016-10-28 14:35:35Z
    1. 应该注意的是,中间案例恰恰是在finally块中包含return语句绝对可怕的原因(它可以隐藏任何Throwable)。
      2010-05-13 11:54:06Z
    2. 想要一个被压抑的
      public static int getMonthsInYear() {
          try {
              return 12;          
          }
          finally {
              throw new RuntimeException();
          }
      }
      
      ? ;)
      2019-02-07 18:06:06Z
    3. 醇>

    我稍微修改了上面的例子 -

     OutOfMemoryError

    以上代码输出:

      

    最终胜过回归。
      2

    这是因为当执行

    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.");
        }
    }
    
    return i;具有值2.此后执行i块,其中12被分配给finally然后执行i out。

    执行System.out块后,finally块返回2,而不是返回12,因为这个return语句不会再次执行。

    如果您将在Eclipse中调试此代码,那么您会感觉在执行try块的System.out后,finally块的return语句再次执行。但这种情况并非如此。它只返回值2。

        
    109
    2015-02-08 04:03:00Z
    1. 这个例子很棒,它添加了几十个最终相关线程中没有提到的东西。我认为几乎没有任何开发人员会知道这一点。
      2016-09-08 09:11:52Z
    2. 如果try不是基元,而是Integer对象,该怎么办。
      2016-09-09 20:50:22Z
    3. 我很难理解这个案例。 docs.oracle.com/javase/specs /jls /se8 /html /jls-14.html#jls-14.17 说,“带有回复声明表达式试图将控制转移到包含它的方法或lambda体的调用者....如果表达式的评估正常完成,产生一个值V ..“我能从这个陈述中猜出 - 似乎返回没有一旦评估了值V,就再次评估表达式,这就是为什么改变了我不会影响返回值,纠正我。
      2016-11-15 18:21:19Z
    4. 但我没有找到任何关于此的证据,其中提到return不会再次评估表达式。
      2016-11-15 18:27:11Z
    5. 2017-05-17 19:18:16Z
    6. 醇>

    以下是 Kevin的回答的详细说明。重要的是要知道要返回的表达式是在i之前计算的,即使它是在之后返回的。

     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");
        }
    }
    
        
    97
    2017-05-23 12:34:53Z
    1. 重要的是要知道。
      2018-07-06 12:06:59Z
    2. 醇>

    这是一个终极块的整个想法。它可以让你确保你做的清理工作,否则你可能会因为你返回而被跳过。

    最后在try块中调用,无论发生什么除非你调用

    X
    finally trumps return... sort of
    0
    
    或Java虚拟机因其他原因而启动)。     
    52
    2015-09-01 17:11:29Z
    1. 这是一个非常微弱的答案。 stackoverflow.com/a/65049/715269
      2019-05-07 18:19:17Z
    2. 醇>

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

    1. 放在finally块中的代码必须在try块
    2. 中执行发生的任何内容
    3. 因此,如果try块中的代码尝试返回值或抛出异常,则该项目将“置于架子上”,直到finally块可以执行
    4. 因为finally块中的代码(根据定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,“架子上”留下的任何东西都将被丢弃。
    5. 唯一的例外是VM在try块期间完全关闭,例如通过'System.exit'
    38
    2008-09-15 19:26:44Z
    1. 这只是“考虑它的逻辑方式”还是真的是如何根据规范工作finally块?在这里,Sun资源的链接非常有趣。
      2010-05-26 19:27:32Z
    2. 2019-05-07 18:25:38Z
    3. 醇>

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

        
    18
    2008-09-15 19:26:15Z

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

        
    18
    2016-01-19 11:39:56Z

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

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

        
    17
    2017-05-03 21:53:37Z

    不,并非总是有一个例外情况// System.exit(0); 在finally块阻止最终执行之前。

     System.exit(0)     
    17
    2018-08-20 06:02:03Z
    1. 这就是你真的不应该调用System.exit()的原因之一......
      2018-09-10 18:34:23Z
    2. 醇>

    最后总是运行这一点,只是因为它在返回后出现在代码中并不意味着它是如何实现的。退出

      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");
            }
        }
    }
    
    块时,Java运行时负责运行此代码。

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

     try

    运行时将生成如下内容:

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

    如果抛出未捕获的异常,

    int foo() {
        int ret = 42;
        System.out.println("done");
        return 42;
    }
    
    块将运行,异常将继续传播。     
    12
    2010-05-13 06:23:53Z

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

     finally     
    10
    2015-08-20 07:00:09Z

    因为除非你调用

    public static int test() {
        int i = 0;
        try {
            return i;
        } finally {
            i = 12;
            System.out.println("finally trumps return.");
            return i;
        }
    }
    
    (或线程崩溃),否则将始终调用finally块。     
    9
    2010-05-13 06:19:17Z
      

    答案很简单

    INPUT:强>

     System.exit()

    输出:强>

     
    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");
    }
    
        
    9
    2017-11-08 08:48:32Z
    1. 答案很简单。
      2017-12-14 08:25:47Z
    2. @ChristopheRoussy怎么样?你能解释一下吗?
      2017-12-14 09:24:49Z
    3. 读取接受的答案,原始问题是“它会永远执行”并且不会总是这样。在你的情况下,它会回答原始问题,甚至可能误导初学者。
      2017-12-14 09:58:15Z
    4. 那么在哪种情况下它不会执行?
      2017-12-15 10:41:08Z
    5. 在其他答案中提到的所有情况下,请参阅1000+ upvotes接受的答案。
      2017-12-18 09:37:37Z
    6. 醇>

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

        
    8
    2008-09-15 17:46:40Z

    简明扼要地,在官方Java文档中(单击此处 ),写的是 -

      

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

        
    8
    2014-10-13 21:51:50Z

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

        
    8
    2014-12-02 12:30:14Z

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

        
    7
    2013-08-16 12:00:20Z

    是的。 只有它不会出现JVM退出或崩溃

        
    7
    2014-01-14 22:29:17Z

    考虑以下计划:

     
    catch
    finally
    

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

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

    这意味着使用

    -abc-
    ---AGAIN---
    -abc-xyz-abc-
    ---PRINT THE RESULT---
    -abc-xyz-abc-xyz
    
    释放对象是一种很好的做法,如下面的代码:  finally     
    7
    2019-02-19 13:37:02Z
    1. 最后不应该是
      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.
          }
      }
      
      吗?
      2018-09-05 06:38:32Z
    2. sb.setLength(0)将清空StringBuffer中的数据。因此,sb = null将取消对象与引用的关联。
      2018-09-05 17:16:56Z
    3. 这不是一个好习惯。最终阻止sb.setLength(0)只是添加了不需要的代码。我知道你的意思是sb = null;块是一个释放资源的好地方,比如数据库连接或类似的东西,但请记住,你的例子可能会让新手感到困惑。
      2019-02-15 23:58:34Z
    4. @ RocBoronat同意。我会编辑我的答案。
      2019-02-16 04:51:39Z
    5. @ Samim谢谢,我添加了第finally System.out.println("---AGAIN2---");行,现在更清楚了。事实上,输出结果与你的论点相反:p我也加入了你的答案,但编辑必须被主持人或类似的人接受。否则你可以添加它们
      2019-02-19 13:08:37Z
    6. 醇>

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

        
    6
    2008-09-15 18:03:05Z
    1. 并非每种语言都有System.out.println(sb); ......
      2015-05-13 08:14:44Z
    2. 醇>

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

        
    6
    2008-10-01 15:15:44Z

    finally将执行,这是肯定的。

    finally将不会在以下情况下执行:

    案例1:

    执行finally时。

    案例2:

    当您的JVM /线程崩溃时。

    案例3:

    手动停止执行时。

        
    6
    2016-11-11 10:39:47Z
    1. 案例3错误,请参阅 stackoverflow.com/a/27117795/2340939
      2015-02-20 09:02:36Z
    2. user2340939:已更正答案
      2015-02-25 05:13:47Z
    3. 醇>
    1. 最后Block总是被执行。除非和直到 那里存在 System.exit()语句(finally块中的第一个语句)。
    2. 如果 system.exit()是第一个语句,那么finally块将不会被执行并且控制来自finally块。 每当System.exit()语句进入finally块时,直到该语句最终执行块,并且当System.exit()出现时,控制力完全从finally块中出来。
    6
    2017-05-20 12:16:09Z
    1. 这已被多次回答,那么你的答案会添加哪些新信息?
      2017-05-20 12:19:36Z
    2. 醇>

    如果你没有处理异常,在终止程序之前,JVM会执行finally块。只有当程序的正常执行失败意味着由于以下原因终止程序时才会执行它。

    1. 通过导致致命错误导致进程中止。

    2. 因内存损坏而终止程序。

    3. 通过调用System.exit()

    4. 如果程序进入无限循环。

    6
    2017-06-08 12:41:40Z

    是的,因为没有控制声明可以阻止System.exit()被执行。

    这是一个参考示例,其中将执行所有代码块:

     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     
    6
    2017-12-16 15:36:36Z

    添加到 @ vibhash的答案,因为没有其他答案解释在如下所示的可变对象的情况下会发生什么

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

    将输出

     
    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 ");
        }
    }
    
        
    6
    2018-04-17 15:35:59Z
    1. 从Java 1.8.162开始,这不是输出。
      2018-04-30 08:28:39Z
    2. 醇>
    sbupdated 
    
来源放置 这里