异常概述及其抛出与捕获机制

一、异常概述

1.1 什么是异常

异常(exception)是在程序执行期间发生的事件,它会破坏程序指令的正常流程。

exception 是 exceptional event 的缩写

当方法中发生错误时,该方法创建一个对象并将其交给运行时系统。该对象称为异常对象(exception object),包含有关错误的信息,包括其类型和错误发生时程序的状态。创建异常对象并将其交给运行时系统称为抛出异常(throw exception)

1.2 引入异常的好处

引入异常处理机制为程序设计提供了许多好处,以下是主要的一些好处:

  • 将错误处理代码与常规代码分开
  • 在调用堆栈中传播错误
  • 分组并区分错误类型

1.3 异常处理流程

在一个方法抛出异常后,运行时系统试图在调用堆栈中搜索包含可以处理异常的代码块的方法。该代码块称为异常处理程序(exception handler)

搜索从发生错误的方法开始,并以调用方法的相反顺序继续通过调用堆栈。当找到合适的处理程序时,运行时系统将异常传递给处理程序。如果抛出的异常对象的类型与处理程序可以处理的类型匹配,则认为异常处理程序是合适的。

在这里插入图片描述

简单来说,异常处理的逻辑遵循调用链,即从方法的调用者向上传递

1.4 异常处理机制的要求

异常通常分为已检查异常(Checked Exceptions)、未检查异常(Unchecked Exceptions)、错误(Errors)。其中,抛出已检查异常的代码必须满足以下两种情况之一

  • 捕获异常(Catch the Exception):使用try语句捕获异常。try语句必须提供异常处理程序,即包含catch块,用于处理可能抛出的异常。

    try {
        // 可能抛出异常的代码
    } catch (SomeException e) {
        // 异常处理代码
    }
    
  • 指定异常(Specify the Exception):方法声明中指定它可能抛出异常。方法必须提供throws子句,列出可能抛出的异常。

    public void someMethod() throws SomeException {
        // 可能抛出异常的代码
    }
    

如果代码不满足上述要求,将无法编译通过。

二、异常类型

2.1 异常类别

异常分为三大类:

  • 已检查异常(Checked Exceptions)
    • 这些异常是编译时异常,必须要么被捕获要么被声明。编译器在在编译阶段会进行检查。
    • 这些是编写良好的应用程序应该预见并从中恢复的异常条件。
    • 例如:FileNotFoundExceptionIOException, SQLException
  • 未检查异常(Unchecked Exceptions)
    • 这些异常是运行时异常,继承自RuntimeException 。未检查异常存在争议
    • 这些异常是应用程序内部的异常情况,应用程序通常无法预测或恢复。这些通常表明编程错误,如逻辑错误或 API 使用不当。
    • 例如:NullPointerException, ArrayIndexOutOfBoundsException
  • 错误(Errors)
    • 这些是由 JVM 引发的严重错误,继承自Error
    • 这些是应用程序外部的异常情况,应用程序通常无法预测或从中恢复。
    • 例如:OutOfMemoryError, StackOverflowError

其中,未检查异常(Unchecked Exceptions) 和错误(Errors)不需要遵循捕获或指定异常的要求。也就是说,针对这两种异常,我们可以不用编写异常处理程序进行处理。

注意:编译时异常是指编译器在编译阶段进行检查的异常类型,但这些异常实际上是在程序运行时可能会发生的。例如,FileNotFoundException 是编译时异常,如果指定的文件在运行时不存在,则会抛出这个异常。

2.2 Exception 类的层次

Throwable 类的直接后代有:

  • Error: 错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
  • Exception:所有的异常类都是 java.lang.Exception 类的子类。

在这里插入图片描述

三、抛出异常

3.1 throws 关键字

throws 关键字是 Java 中用于方法声明的一部分,它指明了该方法可能会抛出某些类型的已检查异常。可以这样理解,方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误

public void readFile(String fileName) throws FileNotFoundException {
    // 方法实现
}

3.2 throw 关键字

throw 关键字可以在代码中抛出一个异常对象。当一个方法使用 throw 抛出异常时,程序控制权会转移到该方法的调用者。调用者需要有适当的 try-catch 结构来捕获并处理该异常。

throw 语句需要一个参数:一个可抛出对象。可抛出对象是 Throwable 类的任何子类的实例。

throw someThrowableObject;

示例如下:

public void validate(int value) {
    if (value < 0) {
        throw new IllegalArgumentException("Negative value not allowed.");
    }
}

3.3 链式异常

应用程序通常通过抛出另一个异常来响应已发生的异常。换句话说,第一个异常可能会导致第二个异常的产生。了解何时一个异常会导致另一个异常是非常重要的,这种机制被称为链式异常(Chained Exception),它有助于程序员追踪异常的源头和原因。

Throwable 常见的支持链式异常的方法如下:

Throwable getCause()
Throwable initCause(Throwable)
Throwable(String, Throwable)
Throwable(Throwable)

示例如下:

try {

} catch (IOException e) {
    throw new SampleException("Other IOException", e);
}

这段代码展示了如何在捕获 IOException 异常后,创建一个新的自定义异常 SampleException,并附加原始原因。然后,将异常链抛出到下一个更高级别的异常处理程序。

3.4 throw 和 throws 的区别

当你在方法中使用 throw 抛出异常时,这表示在特定条件下发生了某种错误。使用 throws 声明异常时,你是在声明这个方法可能会引发错误,以便调用这个方法的代码能做好相应的异常处理。

throw 和 throws 的区别在于:

特性throwthrows
作用抛出一个异常实例声明方法可能抛出的异常类型
使用位置方法内部方法签名(声明部分)
语法格式throw new ExceptionType()void methodName() throws ExceptionType
强制要求不强制要求捕获调用方法时必须捕获或继续声明异常

四、捕获异常(异常处理程序)

在抛出异常后,运行时系统会在调用堆栈中找到异常处理程序来处理该异常。

另外,异常处理程序里面可以在 catch 块里面再次抛出异常与链式异常。通常,希望改变异常的类型时才会这样做。

4.1 try-catch-finally

异常处理程序主要是由异常处理程序组件 try、catch 、finally 来实现。其中:

  • try:用于捕获异常。try 块不能单独存在,后面至少跟着一个 catch块 或者 一个 finally 块,也可以跟着多个 catch 块。
  • catch:处理异常,被称为异常处理程序。另外,单个catch 块可以处理多种类型的异常,不同类型的异常用 |分割
  • finally:用于在异常处理结束后执行清理工作,无论是否抛出异常。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。注意,当 JVM 退出时,finally 块可能不会执行
try {
    // 初始化资源或执行可能抛出异常的操作
    resource = initializeResource()

        // 执行主要操作
        performMainTask(resource)

} catch (SpecificExceptionType1 exception1) {
    // 处理特定类型的异常1
    handleExceptionType1(exception1)

} catch (SpecificExceptionType2 exception2) {
    // 处理特定类型的异常2
    handleExceptionType2(exception2)

} catch (GeneralException exception) {
    // 处理其他一般性异常
    handleGeneralException(exception)

} finally {
    // 清理资源或执行无论如何都会执行的操作
    if (resource is not null) {
        resource.close()
    }
}

4.2 try-with-resources

try-with-resource 语法结构,旨在自动管理资源,确保资源在使用后能够及时关闭,避免资源泄露 。无论代码块中的操作是否成功,资源都会在 try 代码块执行完毕后自动关闭。。

try-with-resources 语句特别适合使用 Closeable 资源的情况,例如流。

try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    // 读取文件的代码
} catch (IOException e) {
    // 处理异常
}

参考资料

Exceptions - Dev.java

Java 异常处理 | 菜鸟教程 (runoob.com)

Java基础常见面试题总结(下) | JavaGuide

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值