Java中的异常丢失与异常链

执键写春秋
• 阅读 1312

1. 异常丢失

1.1 在finally子句中抛出异常。

class MyException1 extends Exception{
    public String toString(){return "测试异常————test1";}
}
class  MyException2 extends Exception{
    public String toString(){return "测试异常————test2";}
}
public class TestException {
    public void test1() throws  MyException1{throw new  MyException1();}
    public void test2() throws  MyException2{throw new  MyException2();}
    public static void main(String[] args) throws  MyException1,  MyException2 {
        TestException c = new TestException();    
        try {
            System.out.println("----------------------外层try------------------------------");
            try{
                System.out.println("----------------------内层try------------------------------");
                c.test1();
            }finally{
                System.out.println("----------------------内层finally------------------------------");
                c.test2();
            }
        }catch(Exception e) {
            System.out.println("----------------------外层catch------------------------------");
            e.printStackTrace();
        }
    }
}
输出:
----------------------外层try------------------------------
----------------------内层try------------------------------
----------------------内层finally------------------------------
----------------------外层catch------------------------------
测试异常————test2
    at person.xsc.datamanage.TestException.test2(TestException.java:10)
    at person.xsc.datamanage.TestException.main(TestException.java:20)
class MyException1 extends Exception{
    public String toString(){return "测试异常————test1";}
}
class  MyException2 extends Exception{
    public String toString(){return "测试异常————test2";}
}
public class TestException {
    public void test1() throws  MyException1{throw new  MyException1();}
    public void test2() throws  MyException2{throw new  MyException2();}
    public static void main(String[] args) throws  MyException1,  MyException2 {
        TestException c = new TestException();    
            try{
                c.test1();
            }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
输出:
测试异常————test1
    at person.xsc.datamanage.TestException.test1(TestException.java:9)
    at person.xsc.datamanage.TestException.main(TestException.java:14)

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

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

package person.xsc.datamanage;
public class TestException {
    public static void main(String[] args) {
            try {
              System.out.println("----------------------try层------------------------------");
              throw new Exception("exception b");
            }finally{
              System.out.println("----------------------finally层------------------------------");
              return ;
            }
    }
}
输出:
----------------------try层------------------------------
----------------------finally层------------------------------

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

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

package person.xsc.datamanage;
public class TestException {
    private static void errorMethod(){           
        try{
            System.out.println("----------------------内层try------------------------------");
            int i = 0;
            int a = 100/i;
        }catch (Exception e){
            System.out.println("----------------------内层catch" + e.getMessage() + "------------------------------");
        }finally {
            System.out.println("----------------------内层finally------------------------------");
        }
    }
    public static void main(String[] args){
        try{
            System.out.println("----------------------外层try------------------------------");
            errorMethod();               
        }catch (Exception e){
            System.out.println("----------------------外层catch" + e.getMessage() + "------------------------------");
        }finally {
            System.out.println("----------------------外层finally------------------------------");
        }
    }
}
输出:
----------------------外层try------------------------------
----------------------内层try------------------------------
----------------------内层catch/ by zero------------------------------
----------------------内层finally------------------------------
----------------------外层finally------------------------------

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

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

package person.xsc.datamanage;
class MyException1 extends Exception{
    public String toString(){return "测试异常————test1";}
}
class  MyException2 extends Exception{
    public String toString(){return "测试异常————test2";}
}
class  MyException3 extends Exception{
    public String toString(){return "测试异常————test3";}
}
public class TestException {
    public void test1() throws  MyException1{throw new  MyException1();}
    public void test2() throws  MyException2{throw new  MyException2();}
    public void test3() throws  MyException3{throw new  MyException3();}
         public static void main(String[]agrs){  
             TestException mt=new TestException() ;
             try{
                 System.out.println("----------------------外层try------------------------------");
                 try{
                     System.out.println("----------------------中间层try------------------------------");
                     try{
                         System.out.println("----------------------内层try------------------------------");
                         mt.test1();
                     }finally{
                         System.out.println("----------------------内层finally------------------------------");
                         mt.test2() ;
                     }
                 }finally{
                     System.out.println("----------------------中间层finally------------------------------");
                     mt.test3() ;
                 }
            }catch(Exception ex){
                System.out.println("----------------------外层catch------------------------------");
                ex.printStackTrace();  
            }
         } 
}
输出:
----------------------外层try------------------------------
----------------------中间层try------------------------------
----------------------内层try------------------------------
----------------------内层finally------------------------------
----------------------中间层finally------------------------------
----------------------外层catch------------------------------
测试异常————test3
    at person.xsc.datamanage.TestException.test3(TestException.java:14)
    at person.xsc.datamanage.TestException.main(TestException.java:30)

观察上面的代码,能发现当多个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 异常链实例

package person.xsc.datamanage;
class MyException1 extends Exception{
    public MyException1(){
        super("这是个异常");
    }
}
public class TryDemoFive {
    public static void test1() throws  MyException1{
        throw new  MyException1();
    }
    public static void test2() throws  Exception{
        try {
            test1();
        } catch (MyException1 e) {
            throw new Exception("我是新产生的异常1",e);
        }
    }
    public static void test3() throws  Exception{
        try {
            test2();
        } catch (Exception e) {
            Exception e1=new Exception("我是新产生的异常2");
            e1.initCause(e);// //实例化异常原因
            throw e1;
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            test3();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
输出:
java.lang.Exception: 我是新产生的异常2
    at person.xsc.datamanage.TryDemoFive.test3(TryDemoFive.java:22)
    at person.xsc.datamanage.TryDemoFive.main(TryDemoFive.java:31)
Caused by: java.lang.Exception: 我是新产生的异常1
    at person.xsc.datamanage.TryDemoFive.test2(TryDemoFive.java:15)
    at person.xsc.datamanage.TryDemoFive.test3(TryDemoFive.java:20)
    ... 1 more
Caused by: person.xsc.datamanage.MyException1: 这是个异常
    at person.xsc.datamanage.TryDemoFive.test1(TryDemoFive.java:9)
    at person.xsc.datamanage.TryDemoFive.test2(TryDemoFive.java:13)
    ... 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块中的代码才不会执行。

  • 说出下面代码存在的问题

    public class TestException {
      public static void start() throws IOException, RuntimeException{     
          throw new RuntimeException("Not able to Start");
      }
    
      public static void main(String args[]) {
          try {
              start();
          } catch (Exception ex) {
              ex.printStackTrace();
          } catch (RuntimeException re) {
              re.printStackTrace();
          }
      }
    }

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

  • 说出下面代码存在的问题

    public class TestException {
      public static void start() throws IOException{     
          throw new RuntimeException("Not able to Start");
      }
    }
    
    

public class Start2 extends TestException{ public static void start() throws Exception{
throw new Exception("Not able to Start"); } }

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

public class TestException { public static void start(){
System.out.print("Not able to Start"); } public static void main(String args[]) { try { start(); }catch(IOException e) { e.printStackTrace(); } } }

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java 项目中如何使用异常
1.早抛出,晚捕获.2.如果finally语句中有return语句,则finally中的return语句将会覆盖try中的return语句,如以下代码,将会输出1。如果在finally语句里有抛出异常,那么此异常将会覆盖try块中抛出的异常。publicclassFinallyReturnTest{
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这