在java中,try ...catch...finally 这个知识点说简单也简单,毕竟只是一个固定结构;说难也难,因为它会出现我们意想之外的结果,这里我摘录些可能出现问题的情况:
情况一:
public class TestTryReturn {
public static void main(String[] args){
TestTryReturn ttr = new TestTryReturn();
System.out.println(ttr.launch());
}
public int launch(){
try{
System.out.println("run in try region");
return 0;
}catch(Exception e){
e.printStackTrace();
System.out.println("run in catch region");
return -1;
}
finally{
System.out.println("run in finally region");
}
}
}
结果:
run in try region
run in finally region
0
情况二
public class TestTryReturn {
public static void main(String[] args){
TestTryReturn ttr = new TestTryReturn();
System.out.println(ttr.launch());
}
public int launch(){
try{
System.out.println("run in try region");
throw new Exception();
}catch(Exception e){
e.printStackTrace();
System.out.println("run in catch region");
return -1;
}
finally{
System.out.println("run in finally region");
}
}
}
结果:
java.lang.Exception
at TestTryReturn.launch(TestTryReturn.java:15)
at TestTryReturn.main(TestTryReturn.java:8)
run in try region
run in catch region
run in finally region
-1
结论:
情况一和情况二,说明无论是在try语句块,还是在catch语句块中,即使存在return,finally也会照样执行。如果一定要强制不让finally执行的话,只有System.exit(-1);
情况三
public class TestException
{
public TestException()
{
}
boolean testEx() throws Exception
{
boolean ret = true;
try
{
ret = testEx1();
}
catch (Exception e)
{
System.out.println("testEx, catch exception");
ret = false;
throw e;
}
finally
{
System.out.println("testEx, finally; return value=" + ret);
return ret;
}
}
boolean testEx1() throws Exception
{
boolean ret = true;
try
{
ret = testEx2();
if (!ret)
{
return false;
}
System.out.println("testEx1, at the end of try");
return ret;
}
catch (Exception e)
{
System.out.println("testEx1, catch exception");
ret = false;
throw e;
}
finally
{
System.out.println("testEx1, finally; return value=" + ret);
return ret;
}
}
boolean testEx2() throws Exception
{
boolean ret = true;
try
{
int b = 12;
int c;
for (int i = 2; i >= -2; i--)
{
c = b / i;
System.out.println("i=" + i);
}
return true;
}
catch (Exception e)
{
System.out.println("testEx2, catch exception");
ret = false;
throw e;
}
finally
{
System.out.println("testEx2, finally; return value=" + ret);
return ret;
}
}
public static void main(String[] args)
{
TestException testException1 = new TestException();
try
{
testException1.testEx();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
结果:
i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false
结论:
在例子中,虽然我们在testEx2中使用throw e抛出了异常,但是由于testEx2中有finally块,而finally块的执行结果是complete abruptly的(别小看这个用得最多的return,它也是一种导致complete abruptly的原因之一啊——后文中有关于导致complete abruptly的原因分析),所以整个try-catch-finally程序块的结果是“complete abruptly”,所以在testEx1中调用testEx2时是捕捉不到testEx1中抛出的那个异常的,而只能将finally中的return结果获取到。
如果在你的代码中期望通过捕捉被调用的下级函数的异常来给定返回值,那么一定要注意你所调用的下级函数中的finally语句,它有可能会使你throw出来的异常并不能真正被上级调用函数可见的。当然这种情况是可以避免的,以testEx2为例:如果你一定要使用finally而且又要将catch中throw的e在testEx1中被捕获到,那么你去掉testEx2中的finally中的return就可以了。
关于abrupt completion
前面提到了complete abruptly(暂且理解为“突然中止”或者“异常结束”吧),它主要包含了两种大的情形:abrupt completion of expressions and statements,下面就分两种情况进行解释。
(1)Normal and Abrupt Completion of Evaluation
每一个表达式(expression)都有一种使得其包含的计算得以一步步进行的正常模式,如果每一步计算都被执行且没有异常抛出,那么就称这个表达式“正常结束(complete normally)”;如果这个表达式的计算抛出了异常,就称为“异常结束(complete abruptly)”。异常结束通常有一个相关联的原因(associated reason),通常也就是抛出一个异常V。
与表达式、操作符相关的运行期异常有:
-->A class instance creation expression, array creation expression , or string concatenation operatior expression throws an OutOfMemoryError if there is insufficient memory available.
-->An array creation expression throws a NegativeArraySizeException if the value of any dimension expression is less than zero.
-->A field access throws a NullPointerException if the value of the object reference expression is null.
-->A method invocation expression that invokes an instance method throws a NullPointerException if the target reference is null.
-->An array access throws a NullPointerException if the value of the array reference expression is null.
-->An array access throws an ArrayIndexOutOfBoundsException if the value of the array index expression is negative or greater than or equal to the length of the array.
-->A cast throws a ClassCastException if a cast is found to be impermissible at run time.
-->An integer division or integer remainder operator throws an ArithmeticException if the value of the right-hand operand expression is zero.
-->An assignment to an array component of reference type throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array.
(2)Normal and Abrupt Completion of Statements
正常情况我们就不多说了,在这里主要是列出了abrupt completion的几种情况:
-->break, continue, and return 语句将导致控制权的转换,从而使得statements不能正常地、完整地执行。
-->某些表达式的计算也可能从java虚拟机抛出异常,这些表达式在上一小节中已经总结过了;一个显式的的throw语句也将导致异常的抛出。抛出异常也是导致控制权的转换的原因(或者说是阻止statement正常结束的原因)。
如果上述事件发生了,那么这些statement就有可能使得其正常情况下应该都执行的语句不能完全被执行到,那么这些statement也就是被称为是complete abruptly.
导致abrupt completion的几种原因:
-->A break with no label
-->A break with a given label
-->A continue with no label
-->A continue with a given label
-->A return with no value
-->A return with a given value A
-->throw with a given value, including exceptions thrown by the Java virtual machine