Java基础之异常


前言

这是一篇学习异常的手记

一、什么是异常?

       如果某个方法不能按照正常的途径完成任务,就可以通过另一种路径退出方法。在这种情况下
会抛出一个封装了错误信息的对象。此时,这个方法会立刻退出同时不返回任何值。另外,调用
这个方法的其他代码也无法继续执行,异常处理机制会将代码执行交给异常处理器。

二、异常的种类和原因

1.异常的种类

       异常是Throwable或其子类之一的实例。Throwable及其所有子类统称为异常类。类ExceptionErrorThrowable的直接子类:

  • Exception 是普通程序可能希望从中恢复的所有异常的超类。
  • Error 是普通程序通常不会从中恢复的所有异常的超类

Exception这种异常又分为两类:运行时异常和编译异常。

1、运行时异常(Unchecked exception):RuntimeException类及其子类表示JVM在运行期间可能出现的错误。比如说试图使用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于运行时,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。

2、受检异常(checked exception):Exception中除RuntimeException及其子类之外的异常。如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,否则编译不通过。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。

继承关系图

2.异常出现的原因

  • 执行了一条throw语句
  • Java虚拟机同步检测到一个异常执行情况,即:
    • 表达式的计算违反了 Java 编程语言的正常语义,例如整数除以零。
    • 加载、链接或初始化程序的一部分时发生错误,在这种情况下,会抛出一个LinkageError子类的实例
    • 内部错误或资源限制阻止 Java 虚拟机实现 Java 编程语言的语义;在这种情况下,会抛出一个VirtualMachineError子类的实例。
  • 发生异步异常
    • 大多数异常作为由于它们的线程的操作而同步发生,而异步异常是可以潜在地发生在一个程序的执行的任意位置
    • 异步异常仅在以下情况下发生:
      • ThreadThreadGroup类的*stop *(不推荐使用的)方法的调用
      • Java 虚拟机中的内部错误或资源限制,阻止其实现 Java 编程语言的语义。在这种情况下,抛出的异步异常是VirtualMachineError 的子类的一个实例。

三.异常的处理

  • 当抛出异常时,控制权从导致异常的代码转移到可以处理异常catch子句。
  • 特定catch子句是否可以处理异常是通过将抛出的对象的类与该catch子句的可捕获异常类进行比较来确定的。如果其可捕获的异常类之一是异常类或异常类的超类,则该子句可以处理异常。
  • 如果找不到 catch可以处理异常的子句,则终止当前线程(遇到异常的线程)。在终止之前,所有finally子句都被执行,未捕获的异常按照以下规则处理:
    • 如果当前线程设置了未捕获的异常处理程序,则执行该处理程序
    • 否则,调用当前线程所属ThreadGroupuncaughtException方法

下面是ThreadGroupuncaughtException方法

public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

四.异常的一些代码细节

Throwable类的部分代码

	//异常信息
    private String detailMessage;
    //当前异常是由哪个Throwable所引起的
    private Throwable cause = this;
	//引起异常的堆栈跟踪信息
    private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
    // 被抑制的异常列表
    private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
  • detailMessage 这个变量是描述异常信息,比如new Myexception(“My Exception”),记录的就是我们传进去的描述此异常的描述信息 My Exception。
  • cause 记录当前异常是由哪个异常所引起的,默认是this,可通过构造器自定义。可以通过initCase方法进行修改。
  • stackTrace 记录当前异常堆栈信息,数组中每一个StackTraceElement表示当前当前方法调用的一个栈帧,表示一次方法调用。StackTraceElement中保存的有当前方法的类名、方法名、文件名、行号信息。
  • suppressedExceptions 保存当前异常抑制的异常列表,在finally代码块抛出异常会抑制catch捕捉的异常,此时可以调用addSuppressed方法将信息携带。

cause的使用

cause 一般用来形成异常链
在这里插入图片描述
在这里插入图片描述

像这样,在catch捕捉异常后抛出一个新的异常,这个新的异常的cause属性catch是捕捉到的异常。所以在使用框架的时候遇到报错直接找最下面的异常,一般那就是bug出现的原因。

stackTrace的使用

stackTrace 的每个元素中都有当前方法的类名、方法名、文件名、行号信息,这些信息被IDE快速定位,只需点一下就可以跳转过去。

去除stackTrace

为什么要去除stackTrace?

       爬栈是抛异常开销大的主要原因之一,当滥用异常来实现某些特殊控制流结构的场景,此时stackTrace肯定是没用的,那个异常对象本身其实也没用,只有它的类型和抛出它带来的控制流跳转才有用。

怎么去除stackTrace?
  • 重载Throwable#fillInStackTrace为直接返回this。
  • 设置writableStackTrace参数为false
    在这里插入图片描述

springboot对stackTrace的一个有趣的使用方式

在这里插入图片描述
SpringApplication类的deduceMainApplicationClass方法手动new了一个异常,然后判断方法的名字是不是main,通过这种方式来推断出MainApplicationClass,我第一次看到是觉得挺有趣的。😁

五.如何正确使用异常

  • 阿里巴巴Java开发手册
    • Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理
    • 异常不要用来做流程控制,条件控制
    • catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理
    • 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容
    • 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
    • finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
    • 不要在 finally 块中使用 return
    • 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类
    • 在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable类来进行拦截

       阿里巴巴Java开发手册github上有

  • Effective Java
    • 只针对异常的情况才使用异常
    • 避免不必要地使用受检异常
    • 优先使用标准的异常
    • 抛出与抽象对应的异常
    • 不要忽略异常

Effective Java 还是很值得读一读的

总结

       这应该算是我第一次写点东西,写得不好是正常的。异常是比较简单的,就这么点知识。

参考资料

  1. java语言规范第11节
  2. 吃透Java基础八:Throwable异常
  3. R大关于重载 Throwable.fillInStackTrace方法以提高Java性能的回答
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值