0%

Java 异常

1. 异常分类

※具体的结构层次如图

  • Throwable

    • Error

      ※Java运行时系统的内部错误和资源耗尽错误,应用程序不该抛出这类异常;
      该异常出现时,只能终止程序

    • Exception

      • IOException 及其他

        ※包含
        试图在文件尾部后面读取数据(IO)
        试图打开一个不存在的文件(IO)
        试图根据给定的字符串查找Class,而这个类并不存在

      • Runtime Exception

        ※包含
        错误的类型转换
        数组访问越界
        访问空指针

  • 注意要点

    • 如果出现 RuntimeException,那么就一定是你的问题,需要从程序设计方面进行改进
    • 所有派生于 ErrorRuntimeException 的异常称为未检查异常
    • 注意 IOException 并不包含用户输入的部分,此类通过一般检查可以避免的异常不应作为异常抛出并处理,应该由流程控制语句(if else while continue)处理

2. 声明异常

类似于C++98的异常规范

  • 在方法的首部声明

    1
    public FileInputStream(String name) throws FileNotFoundException
  • 声明多个时 ,使用逗号隔开

  • 必须声明所有可能抛出的已检查异常

    如果未声明,则表明方法不会抛出(已检查)异常

  • 不必声明未检查异常

  • 子类方法的声明异常不能比超类方法更为通用

    即子类声明的异常层次不能高于超类方法
    超类没有声明异常时,子类也不能声明异常

3.抛出异常

  • 使用 throw 关键字表明抛出异常

    注意与声明异常的关键字(throws)区分开
    只能抛出 Throwable 子类的对象
    而 C++ 能抛出任何类型的值

  • 抛出异常与捕获异常不同,如果没有异常处理器(try catch)捕获异常,则程序将会终止

  • 自定义异常

    • 通常包含一个默认构造器和一个带有详细描述信息的构造器
    • 必须派生于 Exception 及其子类

4.捕获异常

4.1 使用try/catch语句块来捕获异常

如果调用一个抛出已检查异常的方法,则必须对其处理(try/catch),或继续将其传递(throws

一个catch语句里面可以捕获多个异常类型,使用 | 间隔开

此时,异常变量为 final

可以在 catch 语句中再次抛出异常

与 C++ 不同的是,不能只写 throw 关键字,而需要将整个抛出异常都写上

4.2 finally 子句

1
2
3
4
5
6
7
8
9
10
11
12
try
{
...
}
catch(...)
{
...
}
finally
{
...
}

4.2.1 概述

  • 不管是否有异常被捕获,finally 子句都将被执行

  • 抛出异常并被捕获时,先执行 catch 语句,后执行 finally 语句

  • 抛出异常未被捕获时,先执行 finally 语句,后将异常返回给调用者

    try 可以只有 finally,而没有catch

  • 当finally语句抛出异常时,会覆盖掉原有异常(此时建议使用带资源的try语句)

    • ※如果此异常必须返回给调用者的话,则需要进行一些处理才能返回给调用者

    • ※如果原异常具有异常处理器(被捕获)则不需要这种解决办法

      • 常规解决办法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        InputStream in = ....;
        Excepiton ex = null;
        try
        {
        try
        {
        ...
        }
        catch(Exception e)
        {
        ex = e;
        throw e; //这里重新抛出了e,为的就是将这个异常返回给调用者
        }
        }
        finally
        {
        try
        {
        in.close();
        }
        catch(Exception e) //在这里捕获(抑制)了close方法的异常,
        {
        if(ex == null) throw e;
        }
        }

4.2.2 带资源的try语句

  • 只要需要关闭资源,就要尽可能使用带资源的try语句

  • 资源必须属于一个实现了AutoCloseable的类,否则应使用常规方法

    1
    2
    3
    4
    try(Resource res = ...)
    {
    ....
    }
  • 资源:特指文件和输入输出流等,和申请的内存无关

    try 块退出时,将会自动调用 res.close()

  • 可以指定多个资源
    当出现异常时,close 异常会被自动捕获(抑制),原有异常将会重新抛出
    close 的异常将会被增加到原有异常中,可以使用 getSuppressed 方法获取到被抑制的异常列表

try 块也可以有 catchfinally 子句,会在关闭资源之后执行

5. 使用异常的技巧

5.1 异常处理不能代替简单的测试

  • 异常处理会比简单的测试花费更多的时间
  • 应该仅在异常状况下使用异常机制
  • 资源的IO错误,设备错误,物理限制等等
  • 而对于用户的输入错误,应该使用流程控制来进行处理

5.2 不要过分细化异常

应该将整个任务包装在一个try语句内

5.3 利用异常层次结构

※应该尽量的抛出更为恰当的子类,而不是仅仅抛出较为高层次的异常类对象
※捕获时同理

5.4 应该关闭不重要的异常

※在方法多重调用时使用

5.5 早抛出,晚捕获

6. 断言

断言就是增加一个编译检查项,如果结果与断言不符,那么就抛出异常

  • 本文作者: Wafer Li
  • 本文链接: https://wafer.li/Java/Java 异常/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!