java中try{}catch{}和finally{}的执行顺序问题
今天我给大家讲解一下java的的错误和异常处理机制以及相关异常的执行顺序问题。如有不足的地方,欢迎批评指正~
1、首相简单介绍一下java中的错误(Error)和异常(Exception)
错误和异常的介绍:
在java.lang软件包中有一个java.lang.Throwable类,这个类是java中所有错误和异常的超类。
在java中错误和异常的继承主要有两个: 分别为Error和Exception 这两个。
Error: 是java中所有错误类的父类,就是jvm出现错误,以及系统蹦溃等现象,这些错误没办法通过程序来处理,对于系统错误,一般不需要开发
人员处理(也无法处理), 比如内存溢出(Out of Memory)和线程死锁等系统问题。所以在程序中需要使用catch来捕捉处理这类的错误或者使用throw来抛出相
关的异常。
Exception: 又可以分为checkedException(编译时异常) 和RuntimeException(运行时异常) 这两种异常,checkedException异常在进行编译的
时候就可以知道会不会发生异常,如果不对这些异常进行抛出、捕获的话就不能通过编译,如在使用java的io读取文件的时候,可能会会出现
所读取的文件不存在的情况 对于,对于这类编译时的异常必须手动去处理它们(捕获或者抛出)。否则的话是无法正常通过编译器的。 而
RuntimeException就是运行的时候出现的异常,在之前你是没办法确定是不是会出现异常。这类异常仅仅在程序运行的过程中才会发现。
比如数组下标越界(ArrayIndexOutOfBoundsException),强制转换报错(ClassCastException,一部分),空指针异常(NullPointerException)
除数为零(/ by zero) 对于这类运行时的异常是否抛出, 由用户根据自身的情况 决定是否抛出异常。java并不强制要求用户 一定处理。
2、异常处理过程
把会出现异常的程序段放在try中,当抛出异常的时候就会在系统中生成一个异常对象,然后进行查找捕获这个异常,然后进行处理这个异常,处理之后接着执行下面的程序。
出现异常之后如果没有进行捕获处理系统就会直接将这个异常栈的跟踪信息直接打印出来之后就结束这个程序的执行。
对于异常的处理方式有两种分别为:try{}catch{}finally{}和throws下面简单说一下这两者的区别和联系
请先看下面的例子:
public class Test{ public static void main(String[] args){ Test2 test2 = new Test2(); try{ System.out.println("invoke the method begin!"); test2.method(); System.out.println("invoke the method end!"); }catch(Exception e){ System.out.println("catch Exception!"); } } } class Test2{ public void method() throws Exception{ System.out.println("method begin!"); int a = 10; int b = 0; int c = a/b; System.out.println("method end!"); } } 很明显,答案出来了: invoke the method begin! method begin! catch Exception!
对于try{}catch{}finally{}而言,用户可能确定知道代码会出现相关的异常,把有可能出现问题的地方放到try中去执行,如果一旦出现异常,立刻终止当前代码的继续
执行,转而去执行catch{}里面的内容。对于这类异常用户已经处理了,不会在向上抛出。
对于throws而言,一般使用在方法名的后面,使用throws关键字的时候,一般是开发者不确定出现什么异常或者出现异常的情况可能有多种。这时开发者在方法后面加 throws关键字抛出相关的异常。对于调用该方法的其它开发者者必须捕获这个异常或者继续throws这个异常,把这个异常传递下去,交给其对应的父类去处理。
需要注意throw关键字和throws关键字是有区别的。throw一般用于方法中,抛出用户自定义的异常如 throw new MyException("用户自定义异常")。
而throws是用在方法名的后面,通知使用该方法的人,当前方法有可能抛出异常。
如果简单的理解可以这样看:对于throws可以理解为抛出,抛出给别人,自己不处理。而try{}catch{}finally{}则可以理解为截断,开发者自己处理这个异常。
3、异常处理的执行顺序(针对try{}catch{}finally{}而言)
对于try{}catch{}finally{}而言,,它们的执行顺序很简单,如果在try{}中捕获相应的异常,中断当前代码的执行,转而去执行catch{}中的内容,最后去执行
finally{}中方法,一般来说finally中的方法都是会被执行的,其中finally中很大程度上用于资源的释放。
下面讲解一些我们java程序员需要注意的地方。
a、finally中的代码总是会执行吗?
答:no,如果一个方法内在执行try{}语句之前就已经return了,那么finally语句指定不会执行了。因为它根本没有进入try语句中
如果在一个try语句中调用System.exit(0);方法,那么就会退出当前java虚拟机,那么finally也就没有执行的机会了。
b、finally在return之前执行还是在return之后执行?
答:很多人可能会说在return执行之前执行。我的答案是在return中间执行,是不是很特别,请按下面的例子:
package com.yonyou.test; class Test{ public static void main(String[] args) { System.out.println(method()); } public static int method(){ int x=1; try{ return x; }catch(Exception e) { return 0; }finally{ ++x; } } }
请问输出的结果是多少呢?
正确答案是:1
下面我来讲解一下这个程序的执行过程,
首先程序在执行到try{}语句中的return方法后,就会先返回相应的值,并把相应的值存储在一个临时栈中去保存这个结果。这时临时栈中存储的值为1。
但是程序不会立刻返回,转而回去执行finally中的方法,++x,在finally执行完后,方法全部执行完,这时会再次调用return方法,注意这时
不在是返回值,而是告诉主调程序,被调程序已经执行完了,你可以接着去执行你主程序的其它方法了。但是请注意,此时返回的值还是原来保存在临时
栈中的值1。
为了更好的理解这个问题,我们看下面的程序:
package com.yonyou.test; class Test{ public static void main(String[] args) { System.out.println(method()); } public static int method(){ try{ return 1; }catch(Exception e) { return 0; }finally{ return 2; } } }
这时的正确答案又是多少呢?
没错是2,这里仅仅需要注意的是在try{}语句中执行到return 1 会在临时栈中存储值为1的变量。接着回去执行finally里面的内容,这时执行finally中的return 2;方法,这时
临时栈中的值就是变为 2,会覆盖原来临时栈中的值1.所以它的返回值为2。
c、finally方法是必须的吗?
不是,开发者可以根据自身的情况去决定是否使用finally关键字。
1 /** 2 * 描述 : main 方法测试 3 * 4 * @Time : 2020/5/15 8:19 5 */ 6 public class NoSpringTest { 7 8 public static void main(String[] args) { 9 10 System.out.println("============开始测试============"); 11 System.out.println("测试:finally和return 谁先执行"); 12 System.out.println(new NoSpringTest().testFinallyFirst()); 13 System.out.println("测试:try中return后,finally是否会执行"); 14 System.out.println(new NoSpringTest().testFinallySecond()); 15 System.out.println("测试:catch中return后,finally是否会执行"); 16 System.out.println(new NoSpringTest().testFinallyThird()); 17 System.out.println("测试:finally中包含return的话,最终返回哪个值?"); 18 System.out.println(new NoSpringTest().testFinallyForth()); 19 System.out.println("测试:finally修改变量的返回值,是否有效果"); 20 System.out.println(new NoSpringTest().testFinallyFifth()); 21 System.out.println("测试:finally一定会执行吗?"); 22 System.out.println(new NoSpringTest().testFinallySixth()); 23 System.out.println("============结束测试============"); 24 25 } 26 27 28 /** 29 * finally和return 谁先执行 30 * 31 * @return 32 */ 33 public String testFinallyFirst(){ 34 try{ 35 }catch (Exception e){ 36 37 }finally { 38 System.out.println("finally"); 39 } 40 41 return "return"; 42 } 43 44 /** 45 * try中return后,finally是否会执行 46 * 47 * @return 48 */ 49 public String testFinallySecond(){ 50 try{ 51 return "try return"; 52 }catch (Exception e){ 53 54 }finally { 55 System.out.println("finally"); 56 } 57 58 return "normal return"; 59 } 60 61 /** 62 * catch中return后,finally是否会执行 63 * 64 * @return 65 */ 66 public String testFinallyThird(){ 67 try{ 68 long val=1/0; 69 }catch (Exception e){ 70 return "catch return"; 71 72 }finally { 73 System.out.println("finally"); 74 } 75 76 return "normal return"; 77 } 78 79 /** 80 * finally中包含return的话,最终返回哪个值? 81 * 82 * @return 83 */ 84 public String testFinallyForth(){ 85 try{ 86 return "try return"; 87 }catch (Exception e){ 88 89 }finally { 90 System.out.println("finally"); 91 return "finally return"; 92 } 93 94 } 95 96 /** 97 * finally修改变量的返回值,是否有效果 98 * 99 * @return 100 */ 101 public String testFinallyFifth(){ 102 String val="try return"; 103 try{ 104 return val; 105 }catch (Exception e){ 106 107 }finally { 108 System.out.println("finally"); 109 val="finally val"; 110 } 111 return "normal val"; 112 113 } 114 115 /** 116 * finally一定会执行吗? 117 * 118 * @return 119 */ 120 private String testFinallySixth() { 121 if(true){ 122 return "before try return"; 123 } 124 try{ 125 return "try return"; 126 }catch (Exception e){ 127 128 }finally { 129 System.out.println("finally"); 130 } 131 return "normal val"; 132 }
好吧,今天就先到这里吧。
参考链接:https://blog.csdn.net/jdfk423/article/details/80406297