Skip to content

0443 0498 第十二章 异常 Exception

12.2 解决方案 - 异常捕获

  • 如果程序员,认为一段代码可能出现异常/问题,可以使用 try-catch 异常处理机制来解决
  • 从而保证程序的健壮性
  • 如果进行异常处理,那么即使出现了异常,程序可以继续执行
public class Exception01 {
    public static void main(String[] args) {
        int num1 = 10;
        int num2 = 0;//Scanner();
        //1. num1 / num2 => 10 / 0
        //2. 当执行到 num1 / num2 因为 num2 = 0, 程序就会出现(抛出)异常 ArithmeticException
        //3. 当抛出异常后,程序就退出,崩溃了, 下面的代码就不在执行
        //4. 大家想想这样的程序好吗? 不好,不应该出现了一个不算致命的问题,就导致整个系统崩溃
        //5. java 设计者,提供了一个叫 异常处理机制来解决该问题

// int res = num1 / num2;

        //将该代码块->选中->快捷键 ctrl + alt + t -> 选中 try-catch
        //6. 如果进行异常处理,那么即使出现了异常,程序可以继续执行
        try {
            int res = num1 / num2;
        } catch (Exception e) {
            //e.printStackTrace();
            System.out.println("出现异常的原因=" + e.getMessage());//输出异常信息
        }
        System.out.println("程序继续运行....");
    }
}

12.3 异常介绍

基本概念

Java 语言中,将程序执行中发生的不正常情况称为 "异常"。 (开发过程中的语法错误和逻辑错误不是异常)

异常事件分类

  1. Error (错误):Java 虚拟机无法解决的严重问题。

    • 如:JVM 系统内部错误、资源耗尽等严重情况。
    • 如:StackOverflowError 栈溢出和 OOM(out of memory),
    • Error 是严重错误,程序会崩溃
  2. Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。

    • 如:空指针访问,试图读取不存在的文件,网络连接中断等等,
    • Exception 分为两大类:
      • 运行时异常:程序运行时,发生的异常
      • 编译时异常:编程时,编译器检查出的异常
    • 还可分为
      • 非受检异常(Unchecked Exception):如 NullPointerExceptionIllegalArgumentException,由 RuntimeException 及其子类表示,编译器==不强制处理==。(运行时异常)
      • 受检异常(Checked Exception):如 IOExceptionSQLException,需要在代码中==显式处理(try-catch 或 throws==)。(编译时异常)

12.4 异常体系图一览

12.4.1 异常体系图

graph TD
    A[Throwable] --> B[Error]
    A --> C[Exception]

    B --> B1[OutOfMemoryError]
    B --> B2[StackOverflowError]
    B --> B3[VirtualMachineError]

    C --> C1[RuntimeException<br>(非受检异常)]
    C --> C2[IOException<br>(受检异常)]
    C --> C3[SQLException<br>(受检异常)]

    C1 --> C11[NullPointerException]
    C1 --> C12[IllegalArgumentException]
    C1 --> C13[ArithmeticException]
  1. 异常分为两大类,

    • 运行时异常
    • 编译时异常
  2. 运行时异常,编译器检直不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.Iang.RuntimeException类及它的子类都是运行时异常

  3. 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响

  4. 编译时异常,是编译器要求必须处置的异常


12.5 常见运行时异常

运行时异常(Unchecked Exception)是指在程序运行时发生的异常,通常继承自 RuntimeException,编译器不强制要求处理。这些异常通常反映程序逻辑错误,开发者应尽量避免。

  • NullPointerException:空指针异常,访问空对象的成员或方法时抛出。
  • IndexOutOfBoundsException:索引越界异常,例如访问数组或列表超出范围的元素。
  • ArithmeticException:算术异常,例如除以零。
  • IllegalArgumentException:非法参数异常,传递给方法的参数不符合要求。
    • 使用该异常我们可以确保输入是满足条件数字
  • ClassCastException:类型转换异常,尝试将对象强制转换

12.6 常见编译时异常

编译时异常(Checked Exception)是指在编译阶段由编译器强制要求处理的异常,需通过 try-catch 捕获或用 throws 声明抛出,否则代码无法编译通过。这些异常通常继承自 Exception 类(不包括 RuntimeException 及其子类)


12.6.2 常见的编译异常

  • IOException:输入输出操作异常,例如文件读写失败。
  • SQLException:数据库操作相关异常,例如数据库连接失败或查询错误。
  • FileNotFoundException:文件未找到异常,尝试访问不存在的文件时抛出。
  • ClassNotFoundException:类未找到异常,通常在动态加载类时发生。
  • InterruptedException:线程中断异常,例如线程被中断时抛出。
  • EOFException: 输入操作达到文件(或其他输入源)的末尾,并且没有更多数据可供读取时抛出。

EOFException: 以 BufferedReader 为例,可以通过检查 readLine() 方法的返回值是否为 null 来确定是否到达文件末尾


12.8 异常处理

12.8.1 基本介绍

异常处理就是当异常发生时,对异常处理的方式


12.8.2 异常处理的方式

  1. try-catch-finally

    • 程序员在代码中捕获发生的异常,自行处理
  2. throws

    • 将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是 JVM

异常处理示意图

  1. try - catch - finally
graph LR
    A[开始] --> B{try 代码块};
    B -- 正常执行 --> C{finally 代码块};
    B -- 发生异常 --> D{catch 代码块};
    D -- 异常处理成功 --> C;
    D -- 异常未处理 --> C;
    C --> E{异常是否已处理?};
    E -- 是 --> F[程序继续执行];
    E -- 否 --> G[JVM 默认异常处理];
    G --> H[程序崩溃或终止];

  1. throws
graph LR
    A[方法 A] --> B{方法 B throws Exception};
    B -- 发生异常 --> C{方法 A try-catch};
    C -- 捕获并处理 --> D[方法 A 继续执行];
    C -- 未捕获 --> E[调用方法 A 的方法];
    E -- try-catch --> F[处理或继续抛出];
    E -- 无 try-catch --> G[JVM 默认异常处理];
    G --> H[程序崩溃或终止];
    F --> I{异常是否已处理?};
    I -- 是 --> J[程序继续执行];
    I -- 否 --> G;

12.9 try-catch 异常处理

12.9.1 基本介绍

try-catch-finally 用于在代码块中捕获和处理异常,确保程序在异常发生时能妥善处理并执行清理操作。

  • try:包含可能抛出异常的代码。
  • catch:捕获并处理特定类型的异常。
  • finally:无论是否发生异常,都会执行的代码块,通常用于释放资源(如关闭文件)。

12.9.2 处理流程

  1. 执行 try 块中的代码。
  2. 如果发生异常,异常发生后面的代码不会执行,跳转到匹配的 catch 块处理。
  3. 无论是否发生异常,finally 块都会执行(除非程序强制退出,如 System.exit(0))。
  4. 如果无异常,顺序执行 try 块后,进入 finally 块。

12.9.3 补充说明

  1. 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用 finally {}

  2. 可以有多个 catch 语句(可以无 catch),捕获不同的异常(进行不同的业务处理),

    • 要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointerException 在前),
    • 如果发生异常,只会匹配一个 catch
    • 从小分类到大分类
  3. 可以进行 try-finally 配合使用, 这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。


12.9.4 示例代码

try {
    FileInputStream file = new FileInputStream("file.txt");
    // 读取文件
} catch (FileNotFoundException e) {
    System.out.println("文件未找到: " + e.getMessage());
} finally {
    System.out.println("清理资源");
}

12.10 throws 异常处理

12.10.0 [[throws 声明和 throw 方法的区别与联系]]

当方法内部使用 throw 抛出检查异常(如 IOException)或调用其他声明 throws 的方法时,必须在方法签名中使用 throws 声明这些异常。

12.10.1 基本介绍

throws 用于在方法签名中声明可能抛出的异常,将异常处理的责任交给调用者。

  • 作用:方法声明可能抛出的异常类型,调用者需处理这些异常。
  • 适用场景:当方法不适合直接处理异常时,抛给上层调用者处理。

12.10.2 处理流程

  1. 方法通过 throws 声明可能抛出的异常。
  2. 调用者必须使用 try-catch 或继续 throws 处理这些异常。
  3. 如果异常未被处理,可能导致程序终止。

12.10.3 补充说明

  1. 在方法声明中用 throws 语句可以声明抛出异常的列表throws 后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

  2. 对于编译异常,程序中必须处理,比如 try-catch 或者 throws

  3. 对于运行时异常,程序中如果没有处理,默认就是 throws 的方式处理

  4. 子类重写父类的方法时,确出异常的规定:子类重写的方法所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类

  5. throws 过程中,如果有方法 try-catch, 就相当于处理异常,就可以不必 throws

  6. 如果抛出的时运行时异常,则不用处理,因为 JVM 会默认处理,不会报未处理的错误,但是遇到错误会退出程序。


12.10.4 示例代码

public void readFile() throws FileNotFoundException {
    FileInputStream file = new FileInputStream("file.txt");
    // 读取文件
}

// 调用者
try {
    readFile();
} catch (FileNotFoundException e) {
    System.out.println("文件未找到: " + e.getMessage());
}

12.11 自定义异常

12.11.1 基本概念

当程序中出现了某些 "错误",但该错误信息并没有在 Throwable 子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息


12.11.2 自定义异常的步骤

  1. 定义类:自定义异常类名(程序员自己写)继承 Exception 或 RuntimeException
  2. 如果继承Exception, 属于编译异常
  3. 如果继承RuntimeException, 属于运行异常(一般来说,继承RuntimeException)

12.11.3 自定义异常的应用实例

把自定义异常做成运行时异常,好处是,我们可以使用默认的处理机制

public class CustomException {

public static void main(String[] args) /*throws AgeException*/ {

int age = 180;

//要求范围在 18 – 120 之间,否则抛出一个自定义异常if(!(age >= 18 && age <= 120)) {

//这里我们可以通过构造器,设置信息

throw new AgeException("年龄需要在 18~120 之间");

} System.out.println("你的年龄范围正确.");

} }

12.12 throw 和 throws 的区别

![[throws 声明和 throw 方法的区别与联系#3. throwthrows 的区别]]