Java中的异常丢失与异常链

1. 异常丢失

1.1 在finally子句中抛出异常。

1class MyException1 extends Exception{ 2 public String toString(){return "测试异常————test1";} 3} 4class MyException2 extends Exception{ 5 public String toString(){return "测试异常————test2";} 6} 7public class TestException { 8 public void test1() throws MyException1{throw new MyException1();} 9 public void test2() throws MyException2{throw new MyException2();} 10 public static void main(String[] args) throws MyException1, MyException2 { 11 TestException c = new TestException(); 12 try { 13 System.out.println("----------------------外层try------------------------------"); 14 try{ 15 System.out.println("----------------------内层try------------------------------"); 16 c.test1(); 17 }finally{ 18 System.out.println("----------------------内层finally------------------------------"); 19 c.test2(); 20 } 21 }catch(Exception e) { 22 System.out.println("----------------------外层catch------------------------------"); 23 e.printStackTrace(); 24 } 25 } 26} 27输出: 28----------------------外层try------------------------------ 29----------------------内层try------------------------------ 30----------------------内层finally------------------------------ 31----------------------外层catch------------------------------ 32测试异常————test2 33 at person.xsc.datamanage.TestException.test2(TestException.java:10) 34 at person.xsc.datamanage.TestException.main(TestException.java:20)
1class MyException1 extends Exception{ 2 public String toString(){return "测试异常————test1";} 3} 4class MyException2 extends Exception{ 5 public String toString(){return "测试异常————test2";} 6} 7public class TestException { 8 public void test1() throws MyException1{throw new MyException1();} 9 public void test2() throws MyException2{throw new MyException2();} 10 public static void main(String[] args) throws MyException1, MyException2 { 11 TestException c = new TestException(); 12 try{ 13 c.test1(); 14 }catch(Exception e) { 15 e.printStackTrace(); 16 } 17 } 18} 19输出: 20测试异常————test1 21 at person.xsc.datamanage.TestException.test1(TestException.java:9) 22 at person.xsc.datamanage.TestException.main(TestException.java:14) 23

从上面两次代码运行可以看出,内层try抛出的异常被内层finally抛出的异常覆盖掉了。所以,不要在finally子句中抛出异常。

1.2 在finally子句中返回(return)。

1package person.xsc.datamanage; 2public class TestException { 3 public static void main(String[] args) { 4 try { 5 System.out.println("----------------------try层------------------------------"); 6 throw new Exception("exception b"); 7 }finally{ 8 System.out.println("----------------------finally层------------------------------"); 9 return ; 10 } 11 } 12} 13输出: 14----------------------try------------------------------ 15----------------------finally------------------------------

此时会发现try和finally块我们都执行了,但是明明我们在try块抛出了异常,但是最后却没有显示任何异常抛出。所以,如果finally块中包含了return语句,即使try块抛出了异常,但是最后会得到finally块的返回值,并且不会捕获异常。

1.3 观察try catch finally里面try catch finally嵌套执行顺序

1package person.xsc.datamanage; 2public class TestException { 3 private static void errorMethod(){ 4 try{ 5 System.out.println("----------------------内层try------------------------------"); 6 int i = 0; 7 int a = 100/i; 8 }catch (Exception e){ 9 System.out.println("----------------------内层catch" + e.getMessage() + "------------------------------"); 10 }finally { 11 System.out.println("----------------------内层finally------------------------------"); 12 } 13 } 14 public static void main(String[] args){ 15 try{ 16 System.out.println("----------------------外层try------------------------------"); 17 errorMethod(); 18 }catch (Exception e){ 19 System.out.println("----------------------外层catch" + e.getMessage() + "------------------------------"); 20 }finally { 21 System.out.println("----------------------外层finally------------------------------"); 22 } 23 } 24} 25输出: 26----------------------外层try------------------------------ 27----------------------内层try------------------------------ 28----------------------内层catch/ by zero------------------------------ 29----------------------内层finally------------------------------ 30----------------------外层finally------------------------------

内层catch处理了异常,所以没有执行外层catch。这边可以尝试把内层catch注释掉再跑一遍,会发现外层catch会进行异常处理。所以,try catch嵌套,内层不能捕获时,会考虑外层内否捕获,内层能捕获,则外层catch不执行。

1.4 观察 try catch里面嵌套try finally,在try finally里面再嵌套try finally

1package person.xsc.datamanage; 2class MyException1 extends Exception{ 3 public String toString(){return "测试异常————test1";} 4} 5class MyException2 extends Exception{ 6 public String toString(){return "测试异常————test2";} 7} 8class MyException3 extends Exception{ 9 public String toString(){return "测试异常————test3";} 10} 11public class TestException { 12 public void test1() throws MyException1{throw new MyException1();} 13 public void test2() throws MyException2{throw new MyException2();} 14 public void test3() throws MyException3{throw new MyException3();} 15 public static void main(String[]agrs){ 16 TestException mt=new TestException() ; 17 try{ 18 System.out.println("----------------------外层try------------------------------"); 19 try{ 20 System.out.println("----------------------中间层try------------------------------"); 21 try{ 22 System.out.println("----------------------内层try------------------------------"); 23 mt.test1(); 24 }finally{ 25 System.out.println("----------------------内层finally------------------------------"); 26 mt.test2() ; 27 } 28 }finally{ 29 System.out.println("----------------------中间层finally------------------------------"); 30 mt.test3() ; 31 } 32 }catch(Exception ex){ 33 System.out.println("----------------------外层catch------------------------------"); 34 ex.printStackTrace(); 35 } 36 } 37} 38输出: 39----------------------外层try------------------------------ 40----------------------中间层try------------------------------ 41----------------------内层try------------------------------ 42----------------------内层finally------------------------------ 43----------------------中间层finally------------------------------ 44----------------------外层catch------------------------------ 45测试异常————test3 46 at person.xsc.datamanage.TestException.test3(TestException.java:14) 47 at person.xsc.datamanage.TestException.main(TestException.java:30) 48

观察上面的代码,能发现当多个finally块都有异常抛出时,抛出最外层的finally的异常。

1.5 建议

  • 永远不要在finally中抛出异常;
  • 切忌不要在finall语块中使用return。因为try块中的return值会先保存起来,然后执行完finally中的代码后,才会把try块中的return值返回,所以finally中的代码逻辑是不会影响try块中的return值的。但如果在finally中使用return了就会导致try块中的代码得不到执行而无法返回正确的结果。
  • 使用try...finally块时要格外小心,如果可以的话,尽量使用完整的try...catch...finally;
  • 尽量捕获特定的子类,而不是直接捕获Exception类;
  • 不要在catch块中吞掉异常。即对异常不处理,直接return空;

2. 异常链

2.1 异常链定义

两个或者多个不同的异常出现在同一个程序中,并且会发生嵌套抛出,称之为异常链。简单的来说,就是捕获一个异常后抛出另一个异常,并且把原始异常信息保存下来。

2.2 异常链存在意义

随着项目开发的规模越来越大,越往底层,可能抛出的异常类型也会越来越多。如果在上层想要处理这些异常,就需要挨个的写很多catch语句块来捕捉异常,这样是很麻烦的;如果我们对底层抛出的异常捕获后,抛出一个新的统一的异常,的确可以避免这个问题。但是直接抛出一个新的异常,又可能会造成最原始的异常信息丢失,不利于排查问题。因此,如果采用异常链,在保有底层异常信息的基础上,将多层次异常以链路方式进行封装,对后续追查定位BUG是非常有利的。

2.3 异常链实例

1package person.xsc.datamanage; 2class MyException1 extends Exception{ 3 public MyException1(){ 4 super("这是个异常"); 5 } 6} 7public class TryDemoFive { 8 public static void test1() throws MyException1{ 9 throw new MyException1(); 10 } 11 public static void test2() throws Exception{ 12 try { 13 test1(); 14 } catch (MyException1 e) { 15 throw new Exception("我是新产生的异常1",e); 16 } 17 } 18 public static void test3() throws Exception{ 19 try { 20 test2(); 21 } catch (Exception e) { 22 Exception e1=new Exception("我是新产生的异常2"); 23 e1.initCause(e);// //实例化异常原因 24 throw e1; 25 } 26 } 27 public static void main(String[] args) { 28 // TODO Auto-generated method stub 29 try { 30 test3(); 31 } catch (Exception e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 } 35 } 36} 37输出: 38java.lang.Exception: 我是新产生的异常2 39 at person.xsc.datamanage.TryDemoFive.test3(TryDemoFive.java:22) 40 at person.xsc.datamanage.TryDemoFive.main(TryDemoFive.java:31) 41Caused by: java.lang.Exception: 我是新产生的异常1 42 at person.xsc.datamanage.TryDemoFive.test2(TryDemoFive.java:15) 43 at person.xsc.datamanage.TryDemoFive.test3(TryDemoFive.java:20) 44 ... 1 more 45Caused by: person.xsc.datamanage.MyException1: 这是个异常 46 at person.xsc.datamanage.TryDemoFive.test1(TryDemoFive.java:9) 47 at person.xsc.datamanage.TryDemoFive.test2(TryDemoFive.java:13) 48 ... 2 more

2.4 异常链传递过程中,使用Throw带参构造方法和initCause区别?

  • 使用异常的根类Throw所提供的带参构造方法Throwable(String message,Throwable cause)和初始化方法initCause(Throwable cause)都可以实现异常链信息传递。

  • 区别在于initCause方法更加灵活,可以在异常对象构造完成后单独进行异常信息的赋值,在对于异常信息传递作用而言,二者没有区别。

  • 注意A.initCause(B) 语句中A是新抛出的异常,B是捕捉之前方法传入的异常,别搞混淆

3.关于异常的面试问题

  • 如果执行finally代码块之前方法返回了结果,或者JVM退出了,finally块中的代码还会执行吗? 只有在try里面是有System.exit(0)来退出JVM的情况下finally块中的代码才不会执行。
  • 说出下面代码存在的问题
1public class TestException { 2 public static void start() throws IOException, RuntimeException{ 3 throw new RuntimeException("Not able to Start"); 4 } 5 6 public static void main(String args[]) { 7 try { 8 start(); 9 } catch (Exception ex) { 10 ex.printStackTrace(); 11 } catch (RuntimeException re) { 12 re.printStackTrace(); 13 } 14 } 15}

这段代码会在捕捉异常代码块的RuntimeException类型变量“re”里抛出编译异常错误。因为Exception是RuntimeException的超类,在start方法中所有的RuntimeException会被第一个捕捉异常块捕捉,这样就无法到达第二个捕捉块,这就是抛出“exception java.lang.RuntimeException has already been caught”的编译错误原因。所以,如果父类的 catch 块出现在子类的catch 块之前, 就会导致编译错误。因为子类的catch块永远也没有机会运行。

  • 说出下面代码存在的问题
1public class TestException { 2 public static void start() throws IOException{ 3 throw new RuntimeException("Not able to Start"); 4 } 5} 6 7 8public class Start2 extends TestException{ 9 public static void start() throws Exception{ 10 throw new Exception("Not able to Start"); 11 } 12}

这段代码编译器将对子类覆盖start方法产生不满。因为每个Java中方法的覆盖是有规则的,一个覆盖的方法不能抛出的异常比原方法继承关系高。因为这里的start方法在超类中抛出了IOException,所有在子类中的start方法只能抛出要么是IOExcepition或是其子类,但不能是其超类,如Exception。

  • 说出下面代码存在的问题
1public class TestException { 2 public static void start(){ 3 System.out.print("Not able to Start"); 4 } 5 public static void main(String args[]) { 6 try { 7 start(); 8 }catch(IOException e) { 9 e.printStackTrace(); 10 } 11 } 12}

这段代码编译器在处理IOException时会报错,因为没有声明抛出语句,需要start方法进行throws IOException。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )