【Java入门提高篇】Day17 Java异常处理(下)

简介:   今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势。 Exception家族   一图胜千言,先来看一张图。     Exception这是一个父类,它有两个儿子,IOException和RuntimeException...

  今天继续讲解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,你倒是来打我呀(逃)

 

真正重要的东西,用眼睛是看不见的。
相关文章
|
16天前
|
Java
Java中的异常类总结
Java中的异常类总结
|
17天前
|
Java
Java中的throw和throws:异常处理详解
Java中的throw和throws:异常处理详解
21 0
|
24天前
|
Java 程序员 开发者
深入理解Java异常处理机制
在Java编程中,异常处理是确保程序健壮性与稳定性的重要组成部分。本文旨在深度剖析Java异常处理机制的核心概念、结构及其实际应用策略,帮助开发者更好地理解并运用异常处理来优化程序设计。我们将从Java异常体系结构入手,探讨try-catch-finally语句块的执行流程,分析自定义异常的必要性与实现方式,并通过实例演示如何有效地管理和处理异常情况。
23 3
|
25天前
|
关系型数据库 Java 开发工具
Java入门高频考查基础知识9(15问万字参考答案)
本文探讨了Spring Cloud的工作原理,包括注册中心的心跳机制、服务发现机制,以及Eureka默认的负载均衡策略。同时,概述了Spring Boot中常用的注解及其实现方式,并深入讨论了Spring事务的注解、回滚条件、传播性和隔离级别。文章还介绍了MySQL的存储引擎及其区别,特别关注了InnoDB如何实现MySQL的事务处理。此外,本文还详细探讨了MySQL索引,包括B+树的原理和设计索引的方法。最后,比较了Git和SVN的区别,并介绍了Git命令的底层原理及流程。
32 0
Java入门高频考查基础知识9(15问万字参考答案)
|
25天前
|
存储 缓存 算法
Java入门高频考查基础知识4(字节跳动面试题18题2.5万字参考答案)
最重要的是保持自信和冷静。提前准备,并对自己的知识和经验有自信,这样您就能在面试中展现出最佳的表现。祝您面试顺利!Java 是一种广泛使用的面向对象编程语言,在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性,适用于多种应用场景,包括企业应用、移动应用、嵌入式系统等。下是几个面试技巧:复习核心概念、熟悉常见问题、编码实践、项目经验准备、注意优缺点、积极参与互动、准备好问题问对方和知其所以然等,多准备最好轻松能举一反三。
50 0
Java入门高频考查基础知识4(字节跳动面试题18题2.5万字参考答案)
|
25天前
|
存储 算法 JavaScript
Java入门高频考查算法逻辑基础知识3-编程篇(超详细18题1.8万字参考编程实现)
解决这类问题时,建议采取下面的步骤: 理解数学原理:确保你懂得基本的数学公式和法则,这对于制定解决方案至关重要。 优化算法:了解时间复杂度和空间复杂度,并寻找优化的机会。特别注意避免不必要的重复计算。 代码实践:多编写实践代码,并确保你的代码是高效、清晰且稳健的。 错误检查和测试:要为你的代码编写测试案例,测试标准的、边缘情况以及异常输入。 进行复杂问题简化:面对复杂的问题时,先尝试简化问题,然后逐步分析和解决。 沟通和解释:在编写代码的时候清晰地沟通你的思路,不仅要写出正确的代码,还要能向面试官解释你的
33 0
|
25天前
|
存储 Java 编译器
Java入门高频考查基础知识2(超详细28题2.5万字答案)
多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息作出不同的响应。在具体实现上,多态允许一个父类的引用指向其子类的对象,并根据实际指向的对象的类型来调用相应的方法。在 Java 中,多态可以通过以下几种方式实现:在同一个类中,方法名相同,但形参列表不同,实现了多态。子类可以重写(覆盖)其父类的方法,实现多态。在父类引用中调用该方法时,根据实际指向的子类对象的类型来调用相应的方法实现。
39 0
|
26天前
|
编解码 算法 安全
【Java技术专题】「入门到精通系列」深入探索Java技术中常用到的六种加密技术和实现
【Java技术专题】「入门到精通系列」深入探索Java技术中常用到的六种加密技术和实现
44 0
|
29天前
|
Java 程序员 API
Java中的异常处理:理解、实践与最佳实践
在Java编程中,异常处理是一个重要的概念。本文将深入探讨Java中的异常处理,包括其基本概念、如何在实践中应用,以及一些最佳实践。我们将通过实例和代码片段来解析这些概念,以帮助读者更好地理解和应用Java的异常处理。
10 0
|
1月前
|
Java 数据库连接 API
Java 学习路线:基础知识、数据类型、条件语句、函数、循环、异常处理、数据结构、面向对象编程、包、文件和 API
Java 是一种广泛使用的、面向对象的编程语言,始于1995年,以其跨平台性、安全性和可靠性著称,应用于从移动设备到数据中心的各种场景。基础概念包括变量(如局部、实例和静态变量)、数据类型(原始和非原始)、条件语句(if、else、switch等)、函数、循环、异常处理、数据结构(如数组、链表)和面向对象编程(类、接口、继承等)。深入学习还包括包、内存管理、集合框架、序列化、网络套接字、泛型、流、JVM、垃圾回收和线程。构建工具如Gradle、Maven和Ant简化了开发流程,Web框架如Spring和Spring Boot支持Web应用开发。ORM工具如JPA、Hibernate处理对象与数
92 3