原创做品,能够转载,可是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7191280.htmlhtml
一、概述java
Java代码中的异常处理是很是重要的一环,从代码中能够看到,它的使用已经和业务逻辑紧密的结合在一块儿,部分业务逻辑仍是依靠异常来完成的,更多的时候进行异常处理能够完善逻辑,避免可能的出错,规避小错误引起的大停顿。算法
在通常的项目之中,都会自定义运行时异常,用以适应项目的须要,这种异常可被捕捉,也可不被捕捉,它们不会致使整个系统挂掉,可是不少状况下,不捕捉处理就会致使业务出错。spa
在这里咱们模拟几种状况,点明异常捕捉的使用时机。指针
二、状况分析code
先来看没有任何处理的代码htm
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 8 } 9 10 public static void invoke(){ 11 System.out.println("---11---"); 12 int i = 1/0; 13 System.out.println("---12---"); 14 } 15 }
其执行结果以下:blog
---1---
---11---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.donghao.test1.ExceptionTests01.invoke(ExceptionTests01.java:14)
at com.donghao.test1.ExceptionTests01.main(ExceptionTests01.java:7)
解析:main方法调用invoke方法,在执行到第12行时出错,产生算法异常,此时因为无任何异常处理手段,结果就是,程序执行到这里以后直接中断,执行结果中输出的异常堆栈信息是Java内部默认的异常处理机制处理的结果。get
改造一:咱们在invoke方法内部加上异常捕捉机制,代码以下:it
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 8 } 9 10 public static void invoke(){ 11 try{ 12 System.out.println("---11---"); 13 int i = 1/0; 14 }catch(Exception e){ 15 System.out.println("---12---"); 16 } 17 System.out.println("---13---"); 18 } 19 }
执行结果:
---1--- ---11--- ---12--- ---13--- ---2---
结果解析:咱们在invoke方法的执行代码外围添加异常捕捉代码,捕捉Exception异常,这是全部异常的基类,固然也包含这里的算法异常,那么这个捕捉机制就会将1/0产生的异常捕捉到,捕捉到这个异常以后,就会跳转到catch语句块中执行针对这个异常的处理语句,执行完成后,会继续执行try...catch语句块以后的代码,这样的好处显而易见,一处的小错误并不会阻挡整个代码的持续执行,固然若是是严重问题,咱们确实须要暂停执行的,就不能使用这种状况,使用以前的代码就行,因此异常处理机制的执行时机彻底是由项目的业务状况而定的,是很是灵活的,不是固定的死板的。咱们要根据实际的业务场景来合理的使用才是正理。
改造二:咱们在main方法中也添加异常捕捉
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 try{ 5 System.out.println("---1---"); 6 invoke(); 7 }catch(Exception e){ 8 System.out.println("---2---"); 9 } 10 System.out.println("---3---"); 11 } 12 13 public static void invoke(){ 14 try{ 15 System.out.println("---11---"); 16 int i = 1/0; 17 }catch(Exception e){ 18 System.out.println("---12---"); 19 } 20 System.out.println("---13---"); 21 } 22 }
其执行结果以下:
---1--- ---11--- ---12--- ---13--- ---3---
结果几乎与以前的彻底一致,不一样之处在于2没有输出,一是我改变了2的输出位置,并新增了3的输出,如今3至关于以前2的位置,2没有输出的缘由是由于任何一个异常只能被捕捉一次,一旦被捕捉处理,那么以后就不会再次被捕捉,即便我在main方法中将异常类型改为算法异常,也不会捕捉到,异常只会被距离它最近的包含该异常的异常捕捉到,这里的两个异常捕捉其实就是一个嵌套的异常捕捉,并且两者捕捉的异常仍是一致的,通常状况咱们是不会这么使用的,由于毫无心义。但不是说它就彻底不会出现,可能invoke中的代码较长,会有多处异常状况出现,咱们能够在main方法中统一捕捉,而invoke中的异常捕捉只针对单一异常,表示这个异常的出现不会影响invoke方法后面的代码执行,没有异常捕捉的代码一旦出现异常就会中断其后方全部代码的执行(同一代码块内),这个异常会被main方法中的异常捕捉机制捕捉到并执行处理,这样main方法中调用invoke以后的代码仍然能够执行,不会被调用发生异常而中断。
可是若是咱们再将invoke方法中的异常捕捉改变以下:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 try{ 5 System.out.println("---1---"); 6 invoke(); 7 }catch(Exception e){ 8 System.out.println("---2---"); 9 } 10 System.out.println("---3---"); 11 } 12 13 public static void invoke(){ 14 try{ 15 System.out.println("---11---"); 16 int i = 1/0; 17 }catch(NullPointerException e){ 18 System.out.println("---12---"); 19 } 20 System.out.println("---13---"); 21 } 22 }
执行结果发生了变化:
---1--- ---11--- ---2--- ---3---
为何呢?正是由于咱们更改了invoke方法中捕捉的异常类型,以前是异常基类型Exception,如今改为具体的空指针异常,那么这个异常捕捉就只能捕捉空指针异常,它对此处发生的算法异常就会视而不见(因为异常类型的不对口,那么这个异常捕捉至关于没有添加,能够想象成没有异常捕捉的状况),这样就致使invoke方法中在1/0发生异常以后的全部代码所有不会执行,而咱们在main方法中新增的异常捕获却能捕获到这种算法异常,因此12和13都不会输出,而是在异常发生后直接就跳转到main方法中进行异常捕捉,执行catch语句块处理语句输出2,而后是3。
在看一个特殊的状况:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 for(int i = -2;i < 3;i++){ 11 System.out.println("---11---"); 12 System.out.println("12/"+i+"="+12/i); 13 System.out.println("---12---"); 14 } 15 System.out.println("---13---"); 16 } 17 }
invoke方法中是一个循环输出,当第12行发生异常时,循环中断,默认的异常处理机制打印异常堆栈:
---1---
---11---
12/-2=-6
---12---
---11---
12/-1=-12
---12---
---11---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.donghao.test1.ExceptionTests01.invoke(ExceptionTests01.java:14)
at com.donghao.test1.ExceptionTests01.main(ExceptionTests01.java:7)
改造一:在invoke方法的for循环外部添加try...catch:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 try{ 11 for(int i = -2;i < 3;i++){ 12 System.out.println("---11---"); 13 System.out.println("12/"+i+"="+12/i); 14 System.out.println("---12---"); 15 } 16 System.out.println("---13---"); 17 }catch(Exception e){ 18 System.out.println("---14---"); 19 } 20 System.out.println("---15---"); 21 } 22 }
结果:
---1--- ---11--- 12/-2=-6 ---12--- ---11--- 12/-1=-12 ---12--- ---11--- ---14--- ---15--- ---2---
查看结果,发现循环仍是中断了,当i=0时,第13行产生异常,以后循环中断,而后异常才会被for循环以外的异常捕捉到,这种场景也会在实际项目中出现,但很少见,具体场景为,针对循环进行异常捕捉,一旦循环中某一环产生异常,则整个循环终止,处理异常。
改造二:在循环体中加入try...catch块
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 for(int i = -2;i < 3;i++){ 11 try{ 12 System.out.println("---11---"); 13 System.out.println("12/"+i+"="+12/i); 14 System.out.println("---12---"); 15 }catch(Exception e){ 16 System.out.println("---13---"); 17 } 18 System.out.println("---14---"); 19 } 20 System.out.println("---15---"); 21 } 22 }
执行结果:
---1--- ---11--- 12/-2=-6 ---12--- ---14--- ---11--- 12/-1=-12 ---12--- ---14--- ---11--- ---13--- ---14--- ---11--- 12/1=12 ---12--- ---14--- ---11--- 12/2=6 ---12--- ---14--- ---15--- ---2---
这种状况比较多见,咱们将异常捕捉内置到for循环内部,只针对循环体进行异常捕捉,这样当某一次循环体执行时产生了异常,也能私下处理好,不会影响整个循环的继续执行。在循环中还能够结合continue和break关键字进行更加复杂的关系控制,来达到特定的业务需求。
这里我来展现一种状况,也是刚刚作过的一个业务场景:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 for(int i = -2;i < 3;i++){ 11 try{ 12 System.out.println("---11---"); 13 System.out.println("12/"+i+"="+12/i); 14 System.out.println("---12---"); 15 }catch(Exception e){ 16 System.out.println("---13---"); 17 continue; 18 } 19 System.out.println("---14---"); 20 } 21 System.out.println("---15---"); 22 } 23 }
你没看错,只是添加了一个continue;控制信息的展现,当发生异常以后,执行catch块代码,输出13后,再也不执行输出14的操做,而是直接开始新的循环。
有必要作个总结:
1-异常的捕捉是有方向性和类型针对性的,异常会被距离异常发生点最近的包含或者就是捕捉该类型异常的捕捉点捕捉到。这样咱们在作嵌套异常捕捉和多异常捕捉时,就必定要注意要将小范围的异常类型放置到靠进try块的位置,避免大类型劫持异常,致使你设置的异常类型没法生效。
2-咱们将一段代码try...catch包裹,就能够将这段代码从这个方法体中隔离出来,将其影响度降到最低,即便其发生异常,也不会影响到后续代码的执行。
3-throw关键字的配合使用,咱们能够在catch块中使用,表示将捕捉到的异常再次抛出,这里不作处理,这样就必须在方法的调用处再次进行捕捉,能够持续抛出,可是直到最终的方法时,必需要进行处理(对应明确抛出的异常必定要进行捕捉处理,不论你抛几回)。
三、项目的异常处理
异常的转换
项目中咱们都会自定义异常,这些异常通常带有较为明确的目的,甚至咱们可能会在项目中的每一层级定义不一样的异常,这时候就会涉及到异常的转换,其实转换很简单,只要将异常进行捕捉,在catch块中将一行捕捉住,并从新抛出(throw)一个新的异常,将以前的异常信息e做为新异常的参数。
1 try{ 2 int i = 1/0; 3 }catch(Exception e){ 4 throw new RuntimeException(e); 5 }
如上面的例子中,第2行会抛出一个异常,该异常将会被catch块捕捉到,而后内部消化,从新抛出一个RuntimeException,并将原来的异常信息做为新异常的异常信息(即保留原异常信息)。