【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,你倒是来打我呀(逃)

 

真正重要的东西,用眼睛是看不见的。
相关文章
|
2天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
88 60
【Java并发】【线程池】带你从0-1入门线程池
|
2月前
|
Java 开发者
Java中的异常处理:从基础到高级
在Java编程的世界里,异常处理是一块基石,它确保了程序的健壮性和稳定性。本文将带你从异常的基础概念出发,逐步深入到高级处理技巧,通过实例展示如何在Java中有效管理和处理异常。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
2月前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
13天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
72 14
|
1月前
|
自然语言处理 Java
Java中的字符集编码入门-增补字符(转载)
本文探讨Java对Unicode的支持及其发展历程。文章详细解析了Unicode字符集的结构,包括基本多语言面(BMP)和增补字符的表示方法,以及UTF-16编码中surrogate pair的使用。同时介绍了代码点和代码单元的概念,并解释了UTF-8的编码规则及其兼容性。
113 60
|
16天前
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
54 5
|
1月前
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
50 4
|
2月前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
111 7
Spring Boot 入门:简化 Java Web 开发的强大工具
|
2月前
|
Java 开发者
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
105 20
|
2月前
|
监控 架构师 Java
Java虚拟机调优的艺术:从入门到精通####
本文作为一篇深入浅出的技术指南,旨在为Java开发者揭示JVM调优的神秘面纱,通过剖析其背后的原理、分享实战经验与最佳实践,引领读者踏上从调优新手到高手的进阶之路。不同于传统的摘要概述,本文将以一场虚拟的对话形式,模拟一位经验丰富的架构师向初学者传授JVM调优的心法,激发学习兴趣,同时概括性地介绍文章将探讨的核心议题——性能监控、垃圾回收优化、内存管理及常见问题解决策略。 ####

热门文章

最新文章