我运行此代码:
public static void main(String[] args) {
System.out.println(catcher());
}
private static int catcher() {
try {
System.out.println("TRY");
thrower();
return 1;
} catch (Exception e) {
System.out.println("CATCH");
return 2;
} finally {
System.out.println("FINALLY");
return 3;
}
}
private static void thrower() {
throw new RuntimeException();
}
我希望在输出时看到这个:
TRY
CATCH
FINALLY
2
但令人惊讶的是输出是:
TRY
CATCH
FINALLY
3
我很困惑。 return 2
陈述在哪里? return at finally
是一种不良做法吗?
答案 0 :(得分:4)
哪里返回2语句?
它消失了。
具体来说what the JLS says is:
如果catch块由于原因R突然完成,则执行finally块。然后有一个选择:
如果finally块正常完成,则try语句突然完成,原因为R。
如果finally块由于原因S突然完成,则try语句突然完成,原因为S(并且原因R被丢弃)。
(return
语句是abrupt completion的原因。)
最终回归是一种糟糕的做法吗?
基本上是的。因为没有充分的理由这样做,它会吃掉异常。
例如,请注意以下内容:
static void eatAnException() {
try {
throw new RuntimeException("oops");
} finally {
return;
}
}
最后的return语句丢弃了异常。相反,在finally块之后放置一个return语句。
答案 1 :(得分:2)
这并不奇怪。
这是实际行为。返回值在finally
块确定。
如果你最后没有返回任何内容,那么前一个值将成为返回值是返回值。
你可以从字节代码看到,如果你要返回,那么返回值将在finally块中更新。
作为旁注:
the finally block是出于特殊目的。
最终不仅仅用于异常处理 - 它允许程序员避免因返回,继续或中断而意外绕过清理代码。将清理代码放在finally块中总是一种很好的做法,即使没有预期的例外情况也是如此。
不建议在其中编写逻辑。
答案 2 :(得分:1)
Oracle文档(http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html)声明:
“finally块总是在try块退出时执行。这确保即使发生意外异常也会执行finally块。但最终不仅仅是异常处理有用 - 它允许程序员避免通过返回,继续或中断意外绕过清理代码。将清理代码放在finally块中总是一个好习惯,即使没有预期的例外情况。“
答案 3 :(得分:1)
finally块中返回的任何内容实际上都会覆盖try / catch块中的任何异常或返回值
在几个独特的场景中返回后,不会调用finally块:如果首先调用System.exit(),或者JVM崩溃。
这就是为什么在finally块中包含return语句被认为是一个非常糟糕的主意。
答案 4 :(得分:1)
在JSE7语言规范§14.1 中,return
语句被定义为突然终止。如果您的finally
数据块突然完成(您的return
),则try
数据块会因同样的原因(如§14.20.2中所定义)结束:
<强>§14.1强> [...]突然完成总是有一个相关的原因,这是以下之一:[...]没有价值的回报[...]具有给定价值的回报[...]
<强>§14.20.2强>
[...]如果finally块因原因S而突然完成,则try语句突然完成,原因为S(并且原因R被丢弃)。 [...] (原因R是catch
)的结果。
答案 5 :(得分:0)
该行为的原因是finally
块始终已执行,因此当catch
块return
时,您无法结束方法执行1 {}在finally
块中。
所以实际上一步一步地发生了什么:
TRY
转到输出thrower()
方法new RuntimeException()
catch
阻止catcher()
抓住它CATCH
转到输出finally
块FINALLY
转到输出catch
块答案 6 :(得分:0)
编译器在返回之前将“finally”部分(通常是子程序的调用)插入到所有分支中(“当try块退出时”)。这意味着你的方法正在改变如下:
private static int catcher() {
try {
System.out.println("TRY");
thrower();
// -- finally section --
System.out.println("FINALLY");
return 3;
// ---------------------
return 1; // will be eliminated by the compiler
} catch (Exception e) {
System.out.println("CATCH");
// -- finally section --
System.out.println("FINALLY");
return 3;
// ---------------------
return 2; // will be eliminated by the compiler
}
}
这就是你看
的原因TRY 抓住 最后 3
最终回归是一种糟糕的做法吗?
是的,从理论上讲。如果你理解它是如何工作的,你可以在“finally”中使用“return”:)但最好只使用“finally”来释放资源。
还有一个有趣的案例:
private static int catcher2() {
int v = 1;
try {
System.out.println("TRY");
thrower();
return v;
} catch (Exception e) {
System.out.println("CATCH");
v = 2;
return v;
} finally {
System.out.println("FINALLY");
v = 3;
}
}
返回
TRY 抓住 最后 2
因为在字节码中返回之前的最后一个操作(ireturn /“当try块退出时”)是iload(加载一个保存在堆栈中的值,此刻它有“2”)
答案 7 :(得分:0)
无论是否抛出异常,都会调用finally块。在catch块之后,流程返回2以最终阻塞。 finally块再次返回3到主程序。
更多的是你希望“终于”打印出来。紧接着最后的return语句是3.那么你如何期望2被返回。
答案 8 :(得分:0)
如果finally块执行控制转移语句,例如返回或带标记的中断,那么finally块中的return语句返回的值将取代try块或catch中的return语句返回的任何值块。