【Java学习笔记之三十三】详解Java中try,catch,finally的用法及分析

这一篇咱们将会介绍java中try,catch,finally的用法java

如下先给出try,catch用法:express

try {   //须要被检测的异常代码  }
catch(Exception e) { //异常处理,即处理异常代码 }
finally
{

  //必定会被执行的代码

}

代码区若是有错误,就会返回所写异常的处理。编程

首先要清楚,若是没有try的话,出现异常会致使程序崩溃。而try则能够保证程序的正常运行下去,好比说:数组

try
{
    int i = 1/0;
}
catch(Exception e)
{
    ........
}

一个计算的话,若是除数为0,则会报错,若是没有try的话,程序直接崩溃。用try的话,则可让程序运行下去,而且输出为何出错!服务器

try catch 是捕捉try部分的异常,当你没有trycatch的时候,若是出现异常则程序报错,加上try,catch,出现异常程序正常运行,只是把错误信息存储到Exception里,因此catch是用来提取异常信息的,你能够在catch部分加上一句System.out.println(e.ToString());,若是出现异常能够把异常打印出来~~框架

 

java的异常处理机制(try…catch…finally)

1 引子

try…catch…finally恐怕是你们再熟悉不过的语句了,并且感受用起来也是很简单,逻辑上彷佛也是很容易理解。不过,我亲自体验的“教训”告诉我,这个东西可不是想象中的那么简单、听话。不信?那你看看下面的代码,“猜猜”它执行后的结果会是什么?不要日后看答案、也不准执行代码看真正答案哦。若是你的答案是正确,那么这篇文章你就不用浪费时间看啦。less

 1 public class TestException
 2 {
 3     public TestException()
 4     {
 5     }
 6     boolean testEx() throws Exception
 7     {
 8         boolean ret = true;
 9         try
10         {
11             ret = testEx1();
12         }
13         catch (Exception e)
14         {
15             System.out.println("testEx, catch exception");
16             ret = false;
17             throw e;
18         }
19         finally
20         {
21             System.out.println("testEx, finally; return value=" + ret);
22             return ret;
23         }
24     }
25     boolean testEx1() throws Exception
26     {
27         boolean ret = true;
28         try
29         {
30             ret = testEx2();
31             if (!ret)
32             {
33                 return false;
34             }
35             System.out.println("testEx1, at the end of try");
36             return ret;
37         }
38         catch (Exception e)
39         {
40             System.out.println("testEx1, catch exception");
41             ret = false;
42             throw e;
43         }
44         finally
45         {
46             System.out.println("testEx1, finally; return value=" + ret);
47             return ret;
48         }
49     }
50     boolean testEx2() throws Exception
51     {
52         boolean ret = true;
53         try
54         {
55             int b = 12;
56             int c;
57             for (int i = 2; i >= -2; i--)
58             {
59                 c = b / i;
60                 System.out.println("i=" + i);
61             }
62             return true;
63         }
64         catch (Exception e)
65         {
66             System.out.println("testEx2, catch exception");
67             ret = false;
68             throw e;
69         }
70         finally
71         {
72             System.out.println("testEx2, finally; return value=" + ret);
73             return ret;
74         }
75     }
76     public static void main(String[] args)
77     {
78         TestException testException1 = new TestException();
79         try
80         {
81             testException1.testEx();
82         }
83         catch (Exception e)
84         {
85             e.printStackTrace();
86         }
87     }
88 }

你的答案是什么?是下面的答案吗?jvm

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, catch exception
testEx1, finally; return value=false
testEx, catch exception
testEx, finally; return value=false

若是你的答案真的如上面所说,那么你错啦。^_^,那就建议你仔细看一看这篇文章或者拿上面的代码按各类不一样的状况修改、执行、测试,你会发现有不少事情不是原来想象中的那么简单的。
如今公布正确答案:函数

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false

2 基础知识

2.1 相关概念

例外是在程序运行过程当中发生的异常事件,好比除0溢出、数组越界、文件找不到等,这些事件的发生将阻止程序的正常运行。为了增强程序的鲁棒性,程序设计时,必须考虑到可能发生的异常事件并作出相应的处理。C语言中,经过使用if语句来判断是否出现了例外,同时,调用函数经过被调用函数的返回值感知在被调用函数中产生的例外事件并进行处理。全程变量ErroNo经常用来反映一个异常事件的类型。可是,这种错误处理机制会致使很多问题。
Java经过面向对象的方法来处理例外。在一个方法的运行过程当中,若是发生了例外,则这个方法生成表明该例外的一个对象,并把它交给运行时系统,运行时系统寻找相应的代码来处理这一例外。咱们把生成例外对象并把它提交给运行时系统的过程称为抛弃(throw)一个例外。运行时系统在方法的调用栈中查找,从生成例外的方法开始进行回朔,直到找到包含相应例外处理的方法为止,这一个过程称为捕获(catch)一个例外。 测试

2.2 Throwable类及其子类  

用面向对象的方法处理例外,就必须创建类的层次。类 Throwable位于这一类层次的最顶层,只有它的后代才能够作为一个例外被抛弃。图1表示了例外处理的类层次。
从图中能够看出,类Throwable有两个直接子类:Error和Exception。Error类对象(如动态链接错误等),由Java虚拟机生成并抛弃(一般,Java程序不对这类例外进行处理);Exception类对象是Java程序处理或抛弃的对象。它有各类不一样的子类分别对应于不一样类型的例外。其中类RuntimeException表明运行时由Java虚拟机生成的例外,如算术运算例外ArithmeticException(由除0错等致使)、数组越界例外ArrayIndexOutOfBoundsException等;其它则为非运行时例外,如输入输出例外IOException等。Java编译器要求Java程序必须捕获或声明全部的非运行时例外,但对运行时例外能够不作处理。

2.3 异常处理关键字

Java的异常处理是经过5个关键字来实现的:try,catch,throw,throws,finally。JB的在线帮助中对这几个关键字是这样解释的:
Throws:  Lists the exceptions a method could throw.
Throw:   Transfers control of the method to the exception handler.
Try:    Opening exception-handling statement.
Catch:  Captures the exception.
Finally: Runs its code before terminating the program.

2.3.1 try语句 

try语句用大括号{}指定了一段代码,该段代码可能会抛弃一个或多个例外。

2.3.2 catch语句 

catch语句的参数相似于方法的声明,包括一个例外类型和一个例外对象。例外类型必须为Throwable类的子类,它指明了catch语句所处理的例外类型,例外对象则由运行时系统在try所指定的代码块中生成并被捕获,大括号中包含对象的处理,其中能够调用对象的方法。
catch语句能够有多个,分别处理不一样类的例外。Java运行时系统从上到下分别对每一个catch语句处理的例外类型进行检测,直到找到类型相匹配的catch语句为止。这里,类型匹配指catch所处理的例外类型与生成的例外对象的类型彻底一致或者是它的父类,所以,catch语句的排列顺序应该是从特殊到通常。
也能够用一个catch语句处理多个例外类型,这时它的例外类型参数应该是这多个例外类型的父类,程序设计中要根据具体的状况来选择catch语句的例外处理类型。 

2.3.3 finally语句

try所限定的代码中,当抛弃一个例外时,其后的代码不会被执行。经过finally语句能够指定一块代码。不管try所指定的程序块中抛弃或不抛弃例外,也不管catch语句的例外类型是否与所抛弃的例外的类型一致,finally所指定的代码都要被执行,它提供了统一的出口。一般在finally语句中能够进行资源的清除工做。如关闭打开的文件等。

2.3.4 throws语句 

throws老是出如今一个函数头中,用来标明该成员函数可能抛出的各类异常。对大多数Exception子类来讲,Java 编译器会强迫你声明在一个成员函数中抛出的异常的类型。若是异常的类型是Error或 RuntimeException, 或它们的子类,这个规则不起做用, 由于这在程序的正常部分中是不期待出现的。 若是你想明确地抛出一个RuntimeException,你必须用throws语句来声明它的类型。

2.3.5 throw语句 

throw老是出如今函数体中,用来抛出一个异常。程序会在throw语句后当即终止,它后面的语句执行不到,而后在包含它的全部try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。

3 关键字及其中语句流程详解

3.1 try的嵌套

你能够在一个成员函数调用的外面写一个try语句,在这个成员函数内部,写另外一个try语句保护其余代码。每当遇到一个try语句,异常的框架就放到堆栈上面,直到全部的try语句都完成。若是下一级的try语句没有对某种异常进行处理,堆栈就会展开,直到遇到有处理这种异常的try语句。下面是一个try语句嵌套的例子。

 1 class MultiNest {
 2     static void procedure() {
 3         try {
 4             int a = 0;
 5             int b = 42/a;
 6         } catch(java.lang.ArithmeticException e) {
 7             System.out.println("in procedure, catch ArithmeticException: " + e);
 8         }
 9     }
10     public static void main(String args[]) {
11         try {
12             procedure();
13         } catch(java.lang. Exception e) {
14             System.out.println("in main, catch Exception: " + e);
15         }
16     }
17 }

这个例子执行的结果为:

in procedure, catch ArithmeticException: java.lang.ArithmeticException: / by zero

 

成员函数procedure里有本身的try/catch控制,因此main不用去处理 ArrayIndexOutOfBoundsExc

eption;固然若是如同最开始咱们作测试的例子同样,在procedure中catch到异常时使用throw e;语句将异常抛出,那么main固然仍是可以捕捉并处理这个procedure抛出来的异常。例如在procedure函数的catch中的System.out语句后面增长throw e;语句以后,执行结果就变为:

in procedure, catch ArithmeticException: java.lang.ArithmeticException: / by zero
in main, catch Exception: java.lang.ArithmeticException: / by zero

3.2 try-catch程序块的执行流程以及执行结果

相对于try-catch-finally程序块而言,try-catch的执行流程以及执行结果仍是比较简单的。
首先执行的是try语句块中的语句,这时可能会有如下三种状况:
    1.若是try块中全部语句正常执行完毕,那么就不会有其余的“动作”被执行,整个try-catch程序块正常完成。
    2.若是try语句块在执行过程当中碰到异常V,这时又分为两种状况进行处理:
-->若是异常V可以被与try相应的catch块catch到,那么第一个catch到这个异常的catch块(也是离try最近的一个与异常V匹配的catch块)将被执行;若是catch块执行正常,那么try-catch程序块的结果就是“正常完成”;若是该catch块因为缘由R忽然停止,那么try-catch程序块的结果就是“因为缘由R忽然停止(completes abruptly)”。
-->若是异常V没有catch块与之匹配,那么这个try-catch程序块的结果就是“因为抛出异常V而忽然停止(completes abruptly)”。
    3. 若是try因为其余缘由R忽然停止(completes abruptly),那么这个try-catch程序块的结果就是“因为缘由R忽然停止(completes abruptly)”。

3.3 try-catch-finally程序块的执行流程以及执行结果

try-catch-finally程序块的执行流程以及执行结果比较复杂。
首先执行的是try语句块中的语句,这时可能会有如下三种状况:
1.若是try块中全部语句正常执行完毕,那么finally块的居于就会被执行,这时分为如下两种状况:
-->若是finally块执行顺利,那么整个try-catch-finally程序块正常完成。
-->若是finally块因为缘由R忽然停止,那么try-catch-finally程序块的结局是“因为缘由R忽然停止(completes abruptly)”
2.若是try语句块在执行过程当中碰到异常V,这时又分为两种状况进行处理:
-->若是异常V可以被与try相应的catch块catch到,那么第一个catch到这个异常的catch块(也是离try最近的一个与异常V匹配的catch块)将被执行;这时就会有两种执行结果:
-->若是catch块执行正常,那么finally块将会被执行,这时分为两种状况:
-->若是finally块执行顺利,那么整个try-catch-finally程序块正常完成。
-->若是finally块因为缘由R忽然停止,那么try-catch-finally程序块的结局是“因为缘由R忽然停止(completes abruptly)”
-->若是catch块因为缘由R忽然停止,那么finally模块将被执行,分为两种状况:
-->若是若是finally块执行顺利,那么整个try-catch-finally程序块的结局是“因为缘由R忽然停止(completes abruptly)”。
-->若是finally块因为缘由S忽然停止,那么整个try-catch-finally程序块的结局是“因为缘由S忽然停止(completes abruptly)”,缘由R将被抛弃。
(注意,这里就正好和咱们的例子相符合,虽然咱们在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就能够了。
这个事情已经在OMC2.0的MIB中出现过啦:服务器的异常不能彻底被反馈到客户端。)
-->若是异常V没有catch块与之匹配,那么finally模块将被执行,分为两种状况:
-->若是finally块执行顺利,那么整个try-catch-finally程序块的结局就是“因为抛出异常V而忽然停止(completes abruptly)”。
-->若是finally块因为缘由S忽然停止,那么整个try-catch-finally程序块的结局是“因为缘由S忽然停止(completes abruptly)”,异常V将被抛弃。
3.若是try因为其余缘由R忽然停止(completes abruptly),那么finally块被执行,分为两种状况:
-->若是finally块执行顺利,那么整个try-catch-finally程序块的结局是“因为缘由R忽然停止(completes abruptly)”。
-->若是finally块因为缘由S忽然停止,那么整个try-catch-finally程序块的结局是“因为缘由S忽然停止(completes abruptly)”,缘由R将被抛弃。
3.4 try-catch-finally程序块中的return
从上面的try-catch-finally程序块的执行流程以及执行结果一节中能够看出不管try或catch中发生了什么状况,finally都是会被执行的,那么写在try或者catch中的return语句也就不会真正的从该函数中跳出了,它的做用在这种状况下就变成了将控制权(语句流程)转到finally块中;这种状况下必定要注意返回值的处理。
例如,在try或者catch中return false了,而在finally中又return true,那么这种状况下不要期待你的try或者catch中的return false的返回值false被上级调用函数获取到,上级调用函数可以获取到的只是finally中的返回值,由于try或者catch中的return语句只是转移控制权的做用。
3.5 如何抛出异常
若是你知道你写的某个函数有可能抛出异常,而你又不想在这个函数中对异常进行处理,只是想把它抛出去让调用这个函数的上级调用函数进行处理,那么有两种方式可供选择:
第一种方式:直接在函数头中throws SomeException,函数体中不须要try/catch。好比将最开始的例子中的testEx2改成下面的方式,那么testEx1就能捕捉到testEx2抛出的异常了。

 1 boolean testEx2() throws Exception{
 2         boolean ret = true;
 3         int b=12;
 4         int c;
 5         for (int i=2;i>=-2;i--){
 6             c=b/i;
 7             System.out.println("i="+i);
 8         }
 9         return true;   
10 }

第二种方式:使用try/catch,在catch中进行必定的处理以后(若是有必要的话)抛出某种异常。例如上面的testEx2改成下面的方式,testEx1也能捕获到它抛出的异常:

 1 boolean testEx2() throws Exception{
 2         boolean ret = true;
 3         try{
 4             int b=12;
 5             int c;
 6             for (int i=2;i>=-2;i--){
 7                 c=b/i;
 8                 System.out.println("i="+i);
 9             }
10             return true;
11         }catch (Exception e){
12             System.out.println("testEx2, catch exception");
13             Throw e;
14         }
15 }

第三种方法:使用try/catch/finally,在catch中进行必定的处理以后(若是有必要的话)抛出某种异常。例如上面的testEx2改成下面的方式,testEx1也能捕获到它抛出的异常:

 1 boolean testEx2() throws Exception{
 2         boolean ret = true;
 3         try{
 4             int b=12;
 5             int c;
 6             for (int i=2;i>=-2;i--){
 7                 c=b/i;
 8                 System.out.println("i="+i);
 9                 throw new Exception("aaa");
10             }
11             return true;
12         }catch (java.lang.ArithmeticException e){
13             System.out.println("testEx2, catch exception");
14             ret = false;
15             throw new Exception("aaa");
16         }finally{
17             System.out.println("testEx2, finally; return value="+ret);
18         }
19 }

4.关于abrupt completion

前面提到了complete abruptly(暂且理解为“忽然停止”或者“异常结束”吧),它主要包含了两种大的情形:abrupt completion of expressions and statements,下面就分两种状况进行解释。

4.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.

4.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

下面看一个例子(例1),来说解java里面中try、catch、finally的处理流程

 1 public class TryCatchFinally {
 2  
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6  
 7         try {
 8             t = "try";
 9             return t;
10         } catch (Exception e) {
11             // result = "catch";
12             t = "catch";
13             return t;
14         } finally {
15             t = "finally";
16         }
17     }
18  
19     public static void main(String[] args) {
20         System.out.print(TryCatchFinally.test());
21     }
22  
23 }

  首先程序执行try语句块,把变量t赋值为try,因为没有发现异常,接下来执行finally语句块,把变量t赋值为finally,而后return t,则t的值是finally,最后t的值就是finally,程序结果应该显示finally,可是实际结果为try。为何会这样,咱们不妨先看看这段代码编译出来的class对应的字节码,看虚拟机内部是如何执行的。

咱们用javap -verbose TryCatchFinally 来显示目标文件(.class文件)字节码信息

系统运行环境:mac os lion系统 64bit

jdk信息:Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-11M3527) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)

编译出来的字节码部分信息,咱们只看test方法,其余的先忽略掉

 1 public static final java.lang.String test();
 2   Code:
 3    Stack=1, Locals=4, Args_size=0
 4    0:    ldc    #16; //String 
 5    2:    astore_0
 6    3:    ldc    #18; //String try
 7    5:    astore_0
 8    6:    aload_0
 9    7:    astore_3
10    8:    ldc    #20; //String finally
11    10:    astore_0
12    11:    aload_3
13    12:    areturn
14    13:    astore_1
15    14:    ldc    #22; //String catch
16    16:    astore_0
17    17:    aload_0
18    18:    astore_3
19    19:    ldc    #20; //String finally
20    21:    astore_0
21    22:    aload_3
22    23:    areturn
23    24:    astore_2
24    25:    ldc    #20; //String finally
25    27:    astore_0
26    28:    aload_2
27    29:    athrow
28   Exception table:
29    from   to  target type
30     8    13   Class java/lang/Exception
31     8    24   any
32    19    24   any
33   LineNumberTable: 
34    line 5: 0
35    line 8: 3
36    line 9: 6
37    line 15: 8
38    line 9: 11
39    line 10: 13
40    line 12: 14
41    line 13: 17
42    line 15: 19
43    line 13: 22
44    line 14: 24
45    line 15: 25
46    line 16: 28
47 
48   LocalVariableTable: 
49    Start  Length  Slot  Name   Signature
50      27      0    t       Ljava/lang/String;
51      10      1    e       Ljava/lang/Exception;
52 
53   StackMapTable: number_of_entries = 2
54    frame_type = 255 /* full_frame */
55      offset_delta = 13
56      locals = [ class java/lang/String ]
57      stack = [ class java/lang/Exception ]
58    frame_type = 74 /* same_locals_1_stack_item */
59      stack = [ class java/lang/Throwable ]

首先看LocalVariableTable信息,这里面定义了两个变量 一个是t String类型,一个是e Exception 类型

接下来看Code部分

第[0-2]行,给第0个变量赋值“”,也就是String t="";

第[3-6]行,也就是执行try语句块 赋值语句 ,也就是 t = "try";

第7行,重点是第7行,把第s对应的值"try"付给第三个变量,可是这里面第三个变量并无定义,这个比较奇怪

第[8-10] 行,对第0个变量进行赋值操做,也就是t="finally"

第[11-12]行,把第三个变量对应的值返回

经过字节码,咱们发现,在try语句的return块中,return 返回的引用变量(t 是引用类型)并非try语句外定义的引用变量t,而是系统从新定义了一个局部引用t’,这个引用指向了引用t对应的值,也就是try ,即便在finally语句中把引用t指向了值finally,由于return的返回引用已经不是t ,因此引用t的对应的值和try语句中的返回值无关了。

 

下面在看一个例子:(例2)

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             return t;
10         } catch (Exception e) {
11             // result = "catch";
12             t = "catch";
13             return t;
14         } finally {
15             t = "finally";
16             return t;
17         }
18     }
19 
20     public static void main(String[] args) {
21         System.out.print(TryCatchFinally.test());
22     }
23 
24 }

这里稍微修改了 第一段代码,只是在finally语句块里面加入了 一个 return t 的表达式。

按照第一段代码的解释,先进行try{}语句,而后在return以前把当前的t的值try保存到一个变量t',而后执行finally语句块,修改了变量t的值,在返回变量t。

这里面有两个return语句,可是程序到底返回的是try 仍是 finally。接下来咱们仍是看字节码信息

 1 public static final java.lang.String test();
 2   Code:
 3    Stack=1, Locals=2, Args_size=0
 4    0:    ldc    #16; //String 
 5    2:    astore_0
 6    3:    ldc    #18; //String try
 7    5:    astore_0
 8    6:    goto    17
 9    9:    astore_1
10    10:    ldc    #20; //String catch
11    12:    astore_0
12    13:    goto    17
13    16:    pop
14    17:    ldc    #22; //String finally
15    19:    astore_0
16    20:    aload_0
17    21:    areturn
18   Exception table:
19    from   to  target type
20     9     9   Class java/lang/Exception
21    16    16   any
22   LineNumberTable: 
23    line 5: 0
24    line 8: 3
25    line 9: 6
26    line 10: 9
27    line 12: 10
28    line 13: 13
29    line 14: 16
30    line 15: 17
31    line 16: 20
32 
33   LocalVariableTable: 
34    Start  Length  Slot  Name   Signature
35      19      0    t       Ljava/lang/String;
36      6      1    e       Ljava/lang/Exception;
37 
38   StackMapTable: number_of_entries = 3
39    frame_type = 255 /* full_frame */
40      offset_delta = 9
41      locals = [ class java/lang/String ]
42      stack = [ class java/lang/Exception ]
43    frame_type = 70 /* same_locals_1_stack_item */
44      stack = [ class java/lang/Throwable ]
45    frame_type = 0 /* same */

这段代码翻译出来的字节码和第一段代码彻底不一样,仍是继续看code属性

第[0-2]行、[3-5]行第一段代码逻辑相似,就是初始化t,把try中的t进行赋值try

第6行,这里面跳转到第17行,[17-19]就是执行finally里面的赋值语句,把变量t赋值为finally,而后返回t对应的值

咱们发现try语句中的return语句给忽略。可能jvm认为一个方法里面有两个return语句并无太大的意义,因此try中的return语句给忽略了,直接起做用的是finally中的return语句,因此此次返回的是finally。

 

接下来在看看复杂一点的例子:(例3)

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             Integer.parseInt(null);
10             return t;
11         } catch (Exception e) {
12             t = "catch";
13             return t;
14         } finally {
15             t = "finally";
16             // System.out.println(t);
17             // return t;
18         }
19     }
20 
21     public static void main(String[] args) {
22         System.out.print(TryCatchFinally.test());
23     }
24 
25 }

这里面try语句里面会抛出 java.lang.NumberFormatException,因此程序会先执行catch语句中的逻辑,t赋值为catch,在执行return以前,会把返回值保存到一个临时变量里面t ',执行finally的逻辑,t赋值为finally,可是返回值和t',因此变量t的值和返回值已经没有关系了,返回的是catch

例4:

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             Integer.parseInt(null);
10             return t;
11         } catch (Exception e) {
12             t = "catch";
13             return t;
14         } finally {
15             t = "finally";
16             return t;
17         }
18     }
19 
20     public static void main(String[] args) {
21         System.out.print(TryCatchFinally.test());
22     }
23 
24 }

这个和例2有点相似,因为try语句里面抛出异常,程序转入catch语句块,catch语句在执行return语句以前执行finally,而finally语句有return,则直接执行finally的语句值,返回finally

例5:

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             Integer.parseInt(null);
10             return t;
11         } catch (Exception e) {
12             t = "catch";
13             Integer.parseInt(null);
14             return t;
15         } finally {
16             t = "finally";
17             //return t;
18         }
19     }
20 
21     public static void main(String[] args) {
22         System.out.print(TryCatchFinally.test());
23     }
24 
25 }

这个例子在catch语句块添加了Integer.parser(null)语句,强制抛出了一个异常。而后finally语句块里面没有return语句。继续分析一下,因为try语句抛出异常,程序进入catch语句块,catch语句块又抛出一个异常,说明catch语句要退出,则执行finally语句块,对t进行赋值。而后catch语句块里面抛出异常。结果是抛出java.lang.NumberFormatException异常

例子6:

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             Integer.parseInt(null);
10             return t;
11         } catch (Exception e) {
12             t = "catch";
13             Integer.parseInt(null);
14             return t;
15         } finally {
16             t = "finally";
17             return t;
18         }
19     }
20 
21     public static void main(String[] args) {
22         System.out.print(TryCatchFinally.test());
23     }
24 
25 }

这个例子和上面例子中惟一不一样的是,这个例子里面finally 语句里面有return语句块。try catch中运行的逻辑和上面例子同样,当catch语句块里面抛出异常以后,进入finally语句快,而后返回t。则程序忽略catch语句块里面抛出的异常信息,直接返回t对应的值 也就是finally。方法不会抛出异常

例子7:

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             Integer.parseInt(null);
10             return t;
11         } catch (NullPointerException e) {
12             t = "catch";
13             return t;
14         } finally {
15             t = "finally";
16         }
17     }
18 
19     public static void main(String[] args) {
20         System.out.print(TryCatchFinally.test());
21     }
22 
23 }

这个例子里面catch语句里面catch的是NPE异常,而不是java.lang.NumberFormatException异常,因此不会进入catch语句块,直接进入finally语句块,finally对s赋值以后,由try语句抛出java.lang.NumberFormatException异常。

例子8:

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";
 9             Integer.parseInt(null);
10             return t;
11         } catch (NullPointerException e) {
12             t = "catch";
13             return t;
14         } finally {
15             t = "finally";
16             return t;
17         }
18     }
19 
20     public static void main(String[] args) {
21         System.out.print(TryCatchFinally.test());
22     }
23 
24 }

和上面的例子中try catch的逻辑相同,try语句执行完成执行finally语句,finally赋值s 而且返回s ,最后程序结果返回finally

例子9:

 1 public class TryCatchFinally {
 2 
 3     @SuppressWarnings("finally")
 4     public static final String test() {
 5         String t = "";
 6 
 7         try {
 8             t = "try";return t;
 9         } catch (Exception e) {
10             t = "catch";
11             return t;
12         } finally {
13             t = "finally";
14             String.valueOf(null);
15             return t;
16         }
17     }
18 
19     public static void main(String[] args) {
20         System.out.print(TryCatchFinally.test());
21     }
22 
23 }

这个例子中,对finally语句中添加了String.valueOf(null), 强制抛出NPE异常。首先程序执行try语句,在返回执行,执行finally语句块,finally语句抛出NPE异常,整个结果返回NPE异常。

对以上全部的例子进行总结

1 try、catch、finally语句中,在若是try语句有return语句,则返回的以后当前try中变量此时对应的值,此后对变量作任何的修改,都不影响try中return的返回值

2 若是finally块中有return 语句,则返回try或catch中的返回语句忽略。

3 若是finally块中抛出异常,则整个try、catch、finally块中抛出异常

因此使用try、catch、finally语句块中须要注意的是

1 尽可能在try或者catch中使用return语句。经过finally块中达到对try或者catch返回值修改是不可行的。

2 finally块中避免使用return语句,由于finally块中若是使用return语句,会显示的消化掉try、catch块中的异常信息,屏蔽了错误的发生

3 finally块中避免再次抛出异常,不然整个包含try语句块的方法回抛出异常,而且会消化掉try、catch块中的异常

5 关于咱们的编程的一点建议

弄清楚try-catch-finally的执行状况后咱们才能正确使用它。 若是咱们使用的是try-catch-finally语句块,而咱们又须要保证有异常时可以抛出异常,那么在finally语句中就不要使用return语句了(finally语句块的最重要的做用应该是释放申请的资源),由于finally中的return语句会致使咱们的throw e被抛弃,在这个try-catch-finally的外面将只能看到finally中的返回值(除非在finally中抛出异常)。(咱们须要记住:不只throw语句是abrupt completion 的缘由,return、break、continue等这些看起来很正常的语句也是致使abrupt completion的缘由。)

相关文章
相关标签/搜索