不论是在Java还是在Scala中,try..catch
块的finally
都是做一些收尾处理的,比如资源关闭等。如果在finally
语句中如果添加了返回语句,将会发生什么呢?
def getResult: Int = try {
println("try执行了吗?")
return 1
} finally {
println("finally执行了吗?")
return 3
}
运行之后,发现getResult
的返回值是3,反编译后可以看到如下代码:
public int getResult() {
try {
Predef..MODULE$.println("try 执行了吗?");
return 3;
} finally {
Predef..MODULE$.println("finally 执行了吗?");
}
return 3;
}
显然,finally中的代码已经改变了原来方法的执行流程。
try { b } catch e1 finally e2
在Scala中,b的类型是try表达式的期望类型, e1实际上是一个偏函数: scala.PartialFunction[scala.Throwable, pt], pt <: b
, 而e2的类型则是Unit。
上例,finally中的return语句使控制流程又回到了try块中,同时会将finally中的’返回值’作为最终返回值。
下面针对不同情况下,try...catch...finally
的执行进行解释(finally中的代码无论如何都会执行):
(1) 计算b的过程中没有异常
如果在计算表达式e1的过程中抛出异常,那么整个表达式会中止,并且抛出异常(e1表达式中包含的异常)
如果在计算表达式的e1的过程中没有抛出异常,那么b就是整个表达式的返回值
(2) 计算b的过程中有异常(e1中的表达式仍然会被计算)
如果e1的计算过程也抛出异常,那么表达式会中止并且e1过程中的异常被抛出
如果e1的计算过程中没有抛出异常,那么原来b中的异常会在e1计算完成之后再次抛出。
但就像前面提到的,如果在finally中添加返回语句,执行流程发生改变。最终结果则为返回的内容,看上去就好像异常没有被抛出。
def getResult: Int = try {
throw new RuntimeException
} finally {
return 3
}
所以,上面这段代码还是会返回3,通过字节码可以更清晰的了解原因。
public int getResult()
{
// Byte code:
// 0: new 12 java/lang/RuntimeException
// 3: dup
// 4: invokespecial 16 java/lang/RuntimeException:<init> ()V
// 7: athrow
// 8: astore_1
// 9: iconst_3
// 10: ireturn
//
// Exception table:
// from to target type
// 0 8 8 finally
}