• 基础篇:深入解析JAVA异常


    目录

    • 方法中发生异常,会创建一个异常对象交给JVM处理,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并交给JVM的过程称为抛出异常。这会有一系列的方法调用,这系列方法调用的有序列表叫做调用栈
    • JVM会顺着调用栈去查找看是否有可以处理异常的代码,当JVM发现可以处理异常的代码时,会把发生的异常传递给它。如果JVM没有找到可以处理该异常的代码块,JVM就会将该异常转交给默认的异常处理器,默认处理器会打印出异常信息

    1 异常的分类和继承关系

    • Throwable是Java语言中所有错误与异常的超类。Throwable包含两个子类:Error(错误)和Exception(异常)
    • Error是程序中无法处理的,这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,程序不应该去处理此类错误。按照惯例,我们也不应该实现任何新的Error子类的
    • Exception是程序本身可以捕获并且可以处理的异常。Exception又分为两类:运行时异常(RuntimeException)和编译时异常(受检异常)

    2 几种常见异常类的解析

    • RuntimeException(非受检异常)是Java在虚拟机运行期间抛出异常的超类。执行方法期间抛出的RuntimeException的任何子类都无需在throws子句中进行声明,因为它是uncheckedExcepiton。常见五种RuntimeException
      • java.lang.ArithmeticException(算术异常)
      • java.lang.ClassCastException(类型转换异常)
      • java.lang.IllegalArgumentException(不合法的参数异常)
      • java.lang.IndexOutOfBoundsException(数组下标越界异常)
      • java.lang.NullPointerException(空指针异常)
    • 受检异常定义: Exception 中除 RuntimeException 及其子类之外的异常。特点: Java编译器要求程序必须捕获或声明抛出这种异常
      • java.io.IOException(IO流异常)
      • java.lang.ClassNotFoundException(没找到指定类异常)
      • java.lang.NoSuchFieldException(没找到指定字段异常)
      • java.lang.NoSuchMetodException(没找到指定方法异常)
      • java.lang.IllegalAccessException(非法访问异常)
      • java.lang.InterruptedException(中断异常)

    3 Java异常关键字

    关键字作用描述
    try{ } 可能抛出异常的代码放在try语句块内,当try语句块内发生异常时,异常会被抛出
    catch(e) 捕获异常e; catch用来捕获try语句块中发生的异常,可以声明多个catch,catch里也可以捕捉多个异常
    finally finally语句块总会被执行。主要用于回收在try代码块里打开的资源(如数据库连接、TCP连接和文件流)
    throw 用于抛出异常
    throws 声明该方法可能抛出的异常
    • 注意: 执行try、catch或其他地方的return、throw语句前,需要执行finally内的代码。如果在finally中有return、throw语句,则在执行finally里的return或throw语句后,方法结束
    public int hello(String fileName) throws RuntimeException{
        int a = 10;
        try{
            File file = new File(fileName);
            FileInputStream in = new FileInputStream(file);
            return a; // 返回值10
        }catch (FileNotFoundException e){
            System.out.println("error occurred");
        }catch (ArithmeticException | IndexOutOfBoundsException e){
            System.out.println("whatever");
        } finally{
            System.out.println("finally");
            a = 20; // finally修改a,也不会改变try里 return 10 的结局
            // return a; //如果此处返回,则返回值等于 20
        }
        return -1;
    }

    4 开发过程处理异常注意点

    • 抛出明确的异常且对异常进行文档说明
      • 如果方法里有需要外部处理的异常,请声明throws抛出具体异常,方便调用方处理
      • 在方法上声明抛出异常时,也需要进行注释说明。目的是为了给调用者提供尽可能多的信息,方便处理异常
    • 使用具有标识性的消息定义异常:方便精确定位问题
    • 优先捕获最具体的子类异常
      • 如果先catch异常超类,如catch(Exception e),后面捕获catch(RunTimeExcption e)的代码是不会被执行的
    • 不要捕获Throwable类
      • 因为Throwable是Error和Exception超类,Error是JVM、系统级别错误,一般不应捕捉处理
    • 捕捉异常后不要不处理:导致无法定位异常错误的发生根源,建议至少也要日志输出下
    • 不要记录并抛出异常:导致同一个异常输出多条相同日志,不容易找到错误根源
    • 包装新异常时不要丢弃原始的异常
      • 如果丢弃原始的异常,将会丢失堆栈跟踪和原始异常的消息,会使得分析异常事件变得困难
    • 注意:异常会影响性能
      • 异常处理的性能成本非常高,创建一个异常非常慢,抛出一个异常又会消耗1~5ms。尽量不要使用异常来控制代码的逻辑

    5 异常和AutoCloseable(1.7-JDK的语法糖)

    • 在捕捉异常处理,我们经常会在try里打开资源(TCP链接,文件流)。为了防止异常发生而导致资源没被关闭,所以资源的关闭,都需要放在finally代码快里执行
    • 有没有方便的方式呢?在1.7 JDK后,java提供了try–with–resource语法糖,资源对象需要实现AutoCloseable,在try()里打开资源,相关资源就会自动关闭,不再需要手动执行,不管是正常退出或异常退出
    • InputStream子类都继承了AutoCloseable,自动关闭资源类FileInputStream的使用示例
    File file = new File("./text.txt");
    try (FileInputStream in =new FileInputStream(file)){
        ...//数据操作
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        ...
    }

    6 throw和throws的区别

    • throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出
    • throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常

    7 guava的Throwables类了解一下

    • 如果需要对异常调用链操作,可以使用Throwables工具类,Throwables常用方法:
    //以list的方式得到throwable的异常链
    public static List<Throwable> getCausalChain(Throwable throwable)
    //返回最底层的异常
    public static Throwable getRootCause(Throwable throwable)
    //把受检查的异常转换为运行时异常
    //获取异常调用链的描述输出(每一行都有)
    public static String getStackTraceAsString(Throwable throwable)
    //如果异常是declaredType类似,则抛出异常,传递throwable
    public static <X extends Throwable> void propagateIfInstanceOf(
          @Nullable Throwable throwable, Class<X> declaredType) throws X
    //当且仅当它是一个RuntimeException和Error时,或者是一个X的实例时,传递throwable
    void propagateIfPossible(Throwable, Class<X extends Throwable>) throws X
    //将Throwable转为RuntimeException,不建议使用此方法
    public static RuntimeException propagate(Throwable throwable)
    • 实例
    public static void main(String args[]) throws FileNotFoundException {
        try {
            File file = new File("./text.txt");
            FileInputStream inputStream = new FileInputStream(file);
        } catch (Exception e) {
            //获取异常调用栈信息,并打印(或者保存在其他地方)
            String errorMsg = Throwables.getStackTraceAsString(e);
            System.out.println(errorMsg);
            //如果e是FileNotFoundException,直接抛出
            Throwables.propagateIfInstanceOf(e, FileNotFoundException.class);
            //其他Throwable 转为 RuntimeException类抛出
            throw Throwables.propagate(e);
        }
    }

    欢迎指正文中错误

    关注公众号,一起交流

    参考文章

  • 相关阅读:
    VI与VIM区别
    所有的 Unix Like 系统都会内建 vi 文书编辑器。vim 是vi的升级版本,它不仅兼容vi的所有指令 ,而且还有一些新的特性在里面。
    串口bmc
    sed常用
    echo -n -e "请输入重启间隔的时间(分钟): "
    reboot 就是 poweroff 然后power on
    rpm -ql BackupPC |grep etc
    Linux压力测试软件Stress安装及使用指南2
    stress工具使用指南和结果分析(好好好测试通过)
    告别烧脑,金融保险企业邮件应该这样卖产品!
  • 原文地址:https://www.cnblogs.com/cscw/p/13628735.html
Copyright © 2020-2023  润新知