• Java异常处理


    1.异常概述

    异常的定义:异常顾名思义是不同于平常的,异常情况是不正常的情况,异常程序指的是非正常想要的程序。

    假设没有异常处理机制,当程序出现非正常情况时,程序便会直接结束(因为无法继续运行程序打印日志,所以是什么原因导致程序崩溃都不知道);有异常处理时,当程序出现非正常情况时,可以捕获这异常信息并做处理(比如打印错误日志),再看业务情况是否继续运行或结束程序。所以“异常”是属于一种可预测的正常情况。

    异常的作用:异常机制可以使程序中的异常处理代码和正常业务代码分离,保证程序代码更加优雅、有更好的容错和更加健壮。

    2.异常的分类(异常继承体系)

    分为两大类:错误和异常(编译时异常和运行时异常)

    列出常见的几个错误和异常类型。错误和异常的种类很多,Java编程语言所涉及的能力中都有相应的异常类,如有不懂可以查看源码的类注释。

    3.异常的使用

    3.1.异常处理

    编程语言的异常处理基础能力已经成为一门成熟编程语言的标准,除传统的像C语言没有提供异常机制之外,目前主流的编程语言如Java、C++、Python、Go、Ruby等都具备了成熟的异常机制。

    异常的处理:抛出异常(提出问题)和 捕获并处理异常(解决问题)。当程序出现当前环境无法处理产生的问题时,便向上一级抛出问题,寻找能解决问题的环境,直到找到含有合适异常处理的方法并执行,未找到则终止程序。

    Java 异常机制的五个关键字:try 、catch 、finally 、throw 和throws。

    • try关键字后紧跟一个花括号扩起来的代码块(花括号不可省略),用于监听可能引发异常的代码;
    • catch关键字后异常类型和一个异常处理代码块,捕获某一类型的异常和处理这种类型的代码块。catch块可以有多个,表示捕获不同类型异常和处理;
    • finally关键字位于catch块后,用于回收在try块里打开的物理资源,异常机制会保证finally块一定会被执行;
    • throws关键字在方法签名中使用,用于声明该方法可能抛出的异常;
    • throw关键字用于抛出一个实际的异常对象,throw可以单独作为语句使用。

    throw与throws的区别

    throws:在方法声明时使用,声明可能会抛出的一个或多个异常。

    throw:在方法内使用,手动抛出一个异常对象;

    throw与throws的关系

    throw在方法内手动抛出一个异常对象,此时在方法声明中必须使用throws抛出该异常或其父类异常。throw是抛出异常对象,throws是告诉使用者这个方法可能有这个异常。

    3.2.异常的处理流程

    3.3.异常实战

    try……catch

    try……finally

    try……catch……finally

    try……catch……catch……finally

    案例一

    // 检测
    try{
        System.out.println("检测是否有异常");
        float a = 1/0;
        System.out.println("出现异常后,我还可以执行吗");
    }
    // 捕获和处理
    catch (Exception e){
        e.printStackTrace();
        System.out.println("捕获和处理异常");
    }
    System.out.println("继续执行吗?");

     运行结果:

    检测是否有异常 捕获和处理异常 继续执行吗? java.lang.ArithmeticException: / by zero

    结论:当出现异常后,catch捕获处理异常情况,在try块中异常代码后面的代码无法继续执行,但程序可以继续正常运行后续代码。

    案例二

    // 检测
    try{
        System.out.println("检测是否有异常");
        float a = 1/0;
        System.out.println("出现异常后,我还可以执行吗");
    }
    // 结束
    finally{
        System.out.println("做什么都要带上我");
    }
    System.out.println("继续执行吗?");

    运行结果:

    检测是否有异常 做什么都要带上我 Exception in thread "main" java.lang.ArithmeticException: / by zero

    结论:当出现异常后,不catch,只finally,在try块中异常代码后面的代码无法继续执行,并且程序无法正常运行后续代码。

    案例三

    // 检测
    try{
        System.out.println("检测是否有异常");
        float a = 1/0;
        System.out.println("出现异常后,我还可以执行吗");
    }
    // 捕获和处理
    catch (ArithmeticException e){
        e.printStackTrace();
        System.out.println("捕获和处理异常");
    }
    // 结束
    finally{
        System.out.println("做什么都要带上我");
    }
    System.out.println("继续执行吗?");

    运行结果:

    检测是否有异常 捕获和处理异常 做什么都要带上我 继续执行吗? java.lang.ArithmeticException: / by zero

    结论:当出现异常后,catch捕获处理异常情况,在try块中异常代码后面的代码无法继续执行,但程序可以继续正常运行后续代码,并且finally的代码正常执行。

    案例四

    // 检测
    try{
        System.out.println("检测是否有异常");
        float a = 1/0;
        System.out.println("出现异常后,我还可以执行吗");
    }
    // 捕获和处理
    catch (NullPointerException e){
        e.printStackTrace();
        System.out.println("捕获和处理异常:NullPointer");
    }
    // 捕获和处理
    catch (ArithmeticException e){
        e.printStackTrace();
        System.out.println("捕获和处理异常:Arithmetic");
    }
    // 结束
    finally{
        System.out.println("做什么都要带上我");
    }
    System.out.println("继续执行吗?");

    运行结果:

    检测是否有异常 捕获和处理异常:Arithmetic 做什么都要带上我 继续执行吗? java.lang.ArithmeticException: / by zero

    结论:可以使用多个catch块,只有捕获到异常的catch才会执行。

    验证try和catch块中有return 的情况

    正常情况 return

    public static String processEx(){
    
        try{
            System.out.println("检测是否有异常");
            return "try return";
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("处理异常");
            return "catch return";
        }finally{
            System.out.println("做什么都要带上我");
        }
    }
    
    public static void main(String[] args){
        String s = processEx();
        System.out.println(s);
    }

    运行结果:

    检测是否有异常 做什么都要带上我 try return

    结论:执行顺序是try……finally……return;和前面测试一样无异常不进入catch块。

    异常情况 return

    public static String processEx(){
    
        try{
            System.out.println("检测是否有异常");
            float a = 1/0;
            System.out.println("出现异常后,我还可以执行吗");
            return "try return";
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("处理异常");
            return "catch return";
        }finally{
            System.out.println("做什么都要带上我");
        }
    }
    
    public static void main(String[] args){
        String s = processEx();
        System.out.println(s);
    }

    运行结果:

    检测是否有异常 处理异常 做什么都要带上我 catch return java.lang.ArithmeticException: / by zero

    结论:执行顺序是try……catch……finally……return;和前面测试一样,try块异常后不在运行后续代码。

    注意:细心点会发现,这两个案例都不需要在finally中return。因为finally 六亲不认啥都有它分,不建议在finally中return,如果要统一return,那么可以在finally{} 后编写。

    throw在try和catch中的使用

    public static String processEx() {
    
        try{
            System.out.println("检测是否有异常");
            throw new Exception("随便甩个锅");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("这锅我不背");
            return "catch return";
        }finally{
            System.out.println("我也不背");
        }
    }
    
    public static void main(String[] args) {
        String s = processEx();
        System.out.println(s);
    }

    运行结果:

    检测是否有异常 这锅我不背 我也不背 catch return java.lang.Exception: 随便甩个锅

    结论:throw 类似return,有throw后不能再使用return;try只要产生异常,catch 都会尝试捕获处理,这个案例是刚好catch可以处理这个异常,所以不需要在方法声明中往外抛异常,如果catch无法处理将无法通过编译,必须要求在方法声明中使用throws抛出才行,这也体现了编译时异常的好处。

    更多的情况可以自行验证!

    4.自定义异常

    异常三部曲:创建异常类、抛出异常和处理异常(检测(try)、捕获和处理(catch)和结束(finally)),在自定义异常中讲解。

    创建异常类:对问题进行抽象,如:文件名大小限制异常

    public class DemoException extends  Exception{
        // 因为爷爷(Throwable)做了序列化
        private static final long serialVersionUID = 1L;
    
        public DemoException(String massage,Throwable cause){
            // 最终是通过本地方法 fillInStackTrace(0) 来获得堆栈信息;
            super(massage,cause);
        }
    
        // 方便异常类使用
        public DemoException(String massage){
            super(massage,null);
        }
    }

    抛出异常 

    // 抛出异常
    public static void testEx() throws DemoException {
        System.out.println("测试Demo异常");
        throw new DemoException("Demo异常");// 抛出异常时附上的信息
    }

     处理异常

    public static void main(String[] args) {
        try{
            System.out.println("检测是否有异常");
            testEx();
        }catch (DemoException e){
            e.printStackTrace();
            System.out.println("处理异常");
        }finally{
            System.out.println("我想体现我的价值");
        }
    }

    5.异常链

    异常链顾名思义是串联在一起的异常信息。A异常抛给B,B处理了A异常然后抛出了A1异常,C接到A1异常处理了A1然后抛出了A2异常……。

    举个例子

    public class TestDemo2 {
    
        public static void processEx() throws Throwable {
    
            try{
                System.out.println("检测是否有异常");
                throw new Exception("随便甩个锅1");
            }catch (Exception e){
    //            e.printStackTrace();
                System.out.println("处理异常:这锅我不背");
                throw new Exception("随便甩个锅2").initCause(e);
            }finally{
                System.out.println("我也不背");
            }
        }
    
        public static void main(String[] args) {
            try{
                processEx();
            }catch (Throwable e){
                e.printStackTrace();
    //            e.getCause();
            }
        }
    }

    执行结果:

    在编程时经常看到 “Caused by:…… ”,这回知道是什么了吧。

     

     

    Java全栈学习路线、学习资源和面试题一条龙

    学习路线、学习建议和免费经典编程书籍下载:

    GitHub链接:https://github.com/yuantingyue/JavaLearningRoutes-Books

    后续会不断完善学习路线和学习资源,分享更多的知识和更多资源,一起成长,共勉

    想获得最新消息可关注WX公众号:Java全栈布道师

    点赞走起

    原创不易,转载指明出处!!!

  • 相关阅读:
    深入解析DC/OS 1.8 – 高可靠的微服务及大数据管理平台
    Mesos源码分析
    Openvswitch原理与代码分析(8): 修改Openvswitch代码添加自定义action
    Openvswitch原理与代码分析(7): 添加一条流表flow
    Openvswitch原理与代码分析(6):用户态流表flow table的操作
    Openvswitch原理与代码分析(5): 内核中的流表flow table操作
    Openvswitch原理与代码分析(4):网络包的处理过程
    Openvswitch原理与代码分析(3): openvswitch内核模块的加载
    Openvswitch原理与代码分析(2): ovs-vswitchd的启动
    Openvswitch原理与代码分析(1):总体架构
  • 原文地址:https://www.cnblogs.com/dennyLee2025/p/15933992.html
Copyright © 2020-2023  润新知