【问题标题】:Try-finally block with return doesn't rise exception带返回的 Try-finally 块不会引发异常
【发布时间】:2020-07-02 05:46:31
【问题描述】:

在这段代码中,main 方法的 Catch 没有捕获运行时异常。 finally 块执行后,应该已经到了 main 的异常块,但它没有。

 class FinallyDemo {
    static int m1(){
        try{
            System.out.println("Inside m1");
            throw new RuntimeException("hi");
        }
        finally {
            System.out.println("m1 finally");
            return 5;
        }
    }
    public static void main(String[] args) {
        try{
            System.out.println(m1());
        }catch (Exception e){
            System.out.println("main caught: "+ e);
        }
    }
}

输出:

Inside m1
m1 finally
5

【问题讨论】:

  • 你真的得到了上面提到的输出吗?据我所知,void 方法不返回任何值。因此System.out.println(m1()); 会产生编译时异常。
  • 对不起,我已经编辑了代码

标签: java exception try-finally


【解决方案1】:

(如 cmets 中所述,编写的代码甚至无法编译,但可以在不改变问题核心的情况下修复。)

它应该已经进入 main 的异常块,但它没有。

不,它的行为与规范 section 14.20.2 完全相同。那里有很多路径,但这里应用的路径是:

  • ...
  • 如果 try 块的执行由于抛出值 V 而突然完成
    • ...
    • 如果 V 的运行时类型与 try 语句的任何 catch 子句的可捕获异常类的赋值不兼容,则执行 finally 块
      • ...
      • 如果 finally 块由于原因 S 突然完成,那么 try 语句由于原因 S 突然完成(并且值 V 的抛出被丢弃和遗忘)。

根据section 14.1,return 语句算作“突然完成”。所以整个 try/finally 语句由于“返回值”而突然完成,就像没有抛出异常一样。

如果您希望异常传播到 try/finally 语句之外,请不要在 finally 块中使用 return 语句。

【讨论】:

  • 至少在 Eclipse 中,您还会收到一个警告,即 finally 没有“正常”完成。原因在上面的答案中得到了完美的解释。
【解决方案2】:

这是这个类的字节码。

static int m1();
    descriptor: ()I
    flags: ACC_STATIC
    Code:
      stack=3, locals=1, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Inside m1
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: new           #5                  // class java/lang/RuntimeException
        11: dup
        12: ldc           #6                  // String hi
        14: invokespecial #7                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
        17: athrow
        18: astore_0
        19: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: ldc           #8                  // String m1 finally
        24: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: iconst_5
        28: ireturn
      Exception table:
         from    to  target type
             0    19    18   any

审核此计划后。

public class FinallyDemo {
    static void m1(){
        try{
            System.out.println("Inside m1");
            throw new RuntimeException("hi");
        }
        finally {
            System.out.println("m1 finally");
        }
    }
    public static void main(String[] args) {
        try{
            m1();
        }catch (Exception e){
            System.out.println("main caught: "+ e);
        }
    }
}

字节码。

 static void m1();
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=3, locals=1, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Inside m1
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: new           #5                  // class java/lang/RuntimeException
        11: dup
        12: ldc           #6                  // String hi
        14: invokespecial #7                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
        17: athrow
        18: astore_0
        19: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: ldc           #8                  // String m1 finally
        24: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: aload_0
        28: athrow
      Exception table:
         from    to  target type
             0    19    18   any

在我看来,throw 本质上是一个跳转语句。 很明显,在finally中加入return语句,throw语句在编译时就被省略了,因为它被判断为不可达

【讨论】:

  • 如果athrow'statement'在编译时被'省略'它是如何进入字节码的?
  • 我的意思是向外部堆栈帧抛出异常。如果抛出的'statement'没有'省略'。字节码应该是这样的。 27:iconst_5 28:ireturn 29: aload_0 30:athrow
  • 如果不是“省略”,你为什么说它是?下定决心。字节码应该看起来像它看起来的样子,并且说它应该看起来不应该是对它为什么看起来和工作方式的解释。并且athrow 不是“本质上是一个跳转语句”:它在JVM 规范中需要十几行来指定它的真正作用。你的答案完全不正确。
  • 我只是在解释 finally 'statement' 丢弃异常的原因。我知道 athrow 通过期望表工作,我只想指出它们通过 goto 而不是中断机制工作。(它曾经困扰我。)
猜你喜欢
  • 1970-01-01
  • 2020-06-05
  • 2014-01-08
  • 2012-05-02
  • 2017-07-20
  • 2013-02-23
  • 1970-01-01
  • 2012-03-24
  • 1970-01-01
相关资源
最近更新 更多