带你了解Java中的异常处理(下)

 更新时间:2020年08月18日 08:56:52   作者:弗兰克的猫  
这篇文章主要介绍了Java中的异常处理的相关资料,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下

  今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势。

Exception家族

  一图胜千言,先来看一张图。

  Exception这是一个父类,它有两个儿子,IOException和RuntimeException,每个儿子都很能生,所以它有着一堆的孙子,但其实,Exception家族还有一个大家伙,那就是Throwable,这是一个接口,看名字就知道意思,就是“可被抛出”嘛,它还有一个同父异母的哥哥,那就是Error,这家伙可厉害了,Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。catch语句里,不仅可以catch住Exception,还能catch住Error(什么?你真的打算catch Error??程独秀同学,你先坐下。)一般情况下,是不能捕获Error的,对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。除非你有把握能正确处理,否则程独秀同学还是坐下吧(滑稽)。

Unchecked Exception和Checked Exception

  你也许会一脸懵逼,???,这是啥?异常也是分派别的,Unchecked Exception表示“未检查异常“,Checked Exception自然就是”已检查异常“,派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常。那问题来了,为啥要区分这两种异常?

  我们可以再看看上面那个图,可以看出,RuntimeException和Error都是由程序内部引发的错误,比如上一篇里所说的空指针和算术异常。而Checked Exception则大都是由外部因素导致的,如文件无法找到异常,这是虚拟机无法掌控的情况,当出现异常,虚拟机也只能一脸懵逼,不知道该如何是好,所以当有可能发生时,就必须要使用try..catch去捕获它,而对于Unchecked Exception 时,大部分是由于代码引发的,所以只要代码写的足够完善,是不会抛出这样的异常的,所以也不强制要求捕获。

  所以原因其实很简单,编译器将检查你是否为所有的已检查异常提供了异常处理机制,比如说我们使用Class.forName()来查找给定的字符串的class对象的时候,如果没有为这个方法提供异常处理,编译是无法通过的。已检查异常的意义就在于让你知道,这地方是有可能抛异常的,你要注意了,赶紧捕获了。

自定义异常

  那么如何自定义一个异常呢?其实很简单,只需要继承Exception类就好了。看下面的栗子:

public class MyException extends Exception {
 public MyException() {
  super();
 }

 public MyException(String message) {
  super(message);
 }

 public MyException(String message, Throwable cause) {
  super(message, cause);
 }

 public MyException(Throwable cause) {
  super(cause);
 }

 protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
  super(message, cause, enableSuppression, writableStackTrace);
 }
}

  MyException继承了Exception类,重写了构造函数,并没有加自己的逻辑,只是调用了父类的方法。你看,自定义一个异常其实很简单吧。看到这你也许又疑惑了,这尼玛好像就是给Exception换了个名字,有啥用???

  别急,别急,你忘了吗,Exception不仅是可以捕获的,还是可以主动抛出的,所以当遇到某些特定的情况时,我们就可以主动抛出异常,然后在调用时去捕获它,获取异常信息,如果直接用Exception的话,那么捕获的时候,会把所有的异常,该捕获不该捕获的都一起捕获了,那么就没法区分哪些是我们主动抛出来的异常了,这样就无法对那些异常进行特殊处理了。

异常处理的正确姿势  

  接下来要简单介绍一个实际使用中常用的异常处理方法——异常链化处理。

  在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致出现一连串的异常。假设B模块需要调用A模块的方法,如果A模块发生异常,则B也将不能完成而发生异常,但是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。而使用异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。

  异常链化就是用一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。如果你细心一点的话,会发现上面的栗子里也有一个叫做cause的东西,没错,说的其实就是它,在new一个新的异常时,将之前的异常信息传入构造函数即可。下面再用一个简单的栗子进行说明:

public class Test {
 public static void main(String[] args) {
  System.out.println("请输入2个加数");
  int result;
  try {
   result = add();
   System.out.println("结果:"+result);
  } catch (Exception e){
   e.printStackTrace();
  }
 }

 /**
  * 执行加法计算
  */
 private static int add() throws Exception {
  int result;
  try {
   List<Integer> nums =getInputNumbers();
   result = nums.get(0) + nums.get(1);
  }catch(InputMismatchException immExp){
   //链化:以一个异常对象为参数构造新的异常对象。
   throw new Exception("计算失败",immExp);
  }
  return result;
 }

 /**
  * 获取输入的整数
  */
 private static List<Integer> getInputNumbers() {
  List<Integer> nums = new ArrayList<>();
  Scanner scan = new Scanner(System.in);
  try {
   int num1 = scan.nextInt();
   int num2 = scan.nextInt();
   nums.add(new Integer(num1));
   nums.add(new Integer(num2));
  }catch(InputMismatchException immExp){
   throw immExp;
  }finally {
   scan.close();
  }
  return nums;
 }
}

  输出如下:

请输入2个加数
d d
java.lang.Exception: 计算失败
at com.frank.chapter17.Test.add(Test.java:35)
at com.frank.chapter17.Test.main(Test.java:18)
Caused by: java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at com.frank.chapter17.Test.getInputNumbers(Test.java:47)
at com.frank.chapter17.Test.add(Test.java:31)
... 1 more

  可以看到,当输入的不是整数时,发生了异常,在getInputNumbers方法里没有处理这个异常,而是将它继续抛出,在add方法里捕获了异常之后,以该异常为构造参数,重新抛出了一个异常,从打印输出的信息可以看到,不仅仅有第二次抛出的异常信息,第一次的输出信息不匹配异常的详细信息也包含在了里面,衔接在Caused by之后,形成了一条异常链,这样可以方便我们更快的排查问题所在。

  至此,异常就讲解完毕了,希望能给大家带来一些启发和思考,如果觉得还算ok的话,记得动动小手点推荐,让更多人可以看到,也欢迎关注我的博客,会持续更新的。如果有什么讲的不好的地方。。。emmmmmm,你倒是来打我呀(逃)

以上就是带你了解Java中的异常处理(下)的详细内容,更多关于Java 异常处理的资料请关注脚本之家其它相关文章!

相关文章

  • 解决多模块项目中Mybatis的Mapper内部方法找不到的问题

    解决多模块项目中Mybatis的Mapper内部方法找不到的问题

    这篇文章主要介绍了解决多模块项目中Mybatis的Mapper内部方法找不到的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Java 访问剪切板(复制,粘贴)的示例

    Java 访问剪切板(复制,粘贴)的示例

    这篇文章主要介绍了Java 访问剪切板(复制,粘贴)的示例,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-11-11
  • Java内部类及其特点的讲解

    Java内部类及其特点的讲解

    今天小编就为大家分享一篇关于Java内部类及其特点的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • 一文搞懂Java中的抽象类和接口到底是什么

    一文搞懂Java中的抽象类和接口到底是什么

    在类中没有包含足够的信息来描绘一个具体的对象,这样的类称为抽象类,接口是Java中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成,本文给大家介绍Java抽象类和接口,感兴趣的朋友一起看看吧
    2022-04-04
  • java 发送邮件的实例代码(可移植)

    java 发送邮件的实例代码(可移植)

    java 发送邮件的实例代码(可移植),需要的朋友可以参考一下
    2013-03-03
  • Spring解决循环依赖的方法(三级缓存)

    Spring解决循环依赖的方法(三级缓存)

    今天,我们要说的是spring是如何解决循环依赖的。对于一个问题说解决之前,我们首先要先明确形成问题的本因。那么循环依赖,何为循环依赖呢?感兴趣的朋友跟随小编一起看看吧
    2021-11-11
  • 简单总结Java IO中stream流的使用方法

    简单总结Java IO中stream流的使用方法

    这篇文章主要介绍了Java IO中stream流的使用方法的简单总结,包括数据流和打印流等Java入门学习中的基础知识,需要的朋友可以参考下
    2016-03-03
  • Java运算符从见过到掌握上

    Java运算符从见过到掌握上

    计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量,本篇对大家的学习或工作具有一定的价值,需要的朋友可以参考下
    2021-09-09
  • Spring Boot整合Spring Cache及Redis过程解析

    Spring Boot整合Spring Cache及Redis过程解析

    这篇文章主要介绍了Spring Boot整合Spring Cache及Redis过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java常用命令汇总

    Java常用命令汇总

    这篇文章主要介绍了Java常用命令汇总,小编觉得挺不错的,这里给大家分享下,供需要的朋友参考。
    2017-10-10

最新评论