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 语言中,将程序执行中发生的不正常情况称为 "异常"。 (开发过程中的语法错误和逻辑错误不是异常)
异常事件分类¶
-
Error (错误):Java 虚拟机无法解决的严重问题。
- 如:JVM 系统内部错误、资源耗尽等严重情况。
- 如:
StackOverflowError栈溢出和OOM(out of memory), - Error 是严重错误,程序会崩溃
-
Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
- 如:空指针访问,试图读取不存在的文件,网络连接中断等等,
- Exception 分为两大类:
- 运行时异常:程序运行时,发生的异常
- 编译时异常:编程时,编译器检查出的异常
- 还可分为
- 非受检异常(Unchecked Exception):如
NullPointerException、IllegalArgumentException,由RuntimeException及其子类表示,编译器==不强制处理==。(运行时异常) - 受检异常(Checked Exception):如
IOException、SQLException,需要在代码中==显式处理(try-catch 或 throws==)。(编译时异常)
- 非受检异常(Unchecked Exception):如
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]
-
异常分为两大类,
- 运行时异常
- 编译时异常
-
运行时异常,编译器检直不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。
java.Iang.RuntimeException类及它的子类都是运行时异常 -
对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响
-
编译时异常,是编译器要求必须处置的异常。
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 异常处理的方式¶
-
try-catch-finally- 程序员在代码中捕获发生的异常,自行处理
-
throws- 将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是 JVM
异常处理示意图¶
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[程序崩溃或终止];
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 处理流程¶
- 执行
try块中的代码。 - 如果发生异常,异常发生后面的代码不会执行,跳转到匹配的
catch块处理。 - 无论是否发生异常,
finally块都会执行(除非程序强制退出,如System.exit(0))。 - 如果无异常,顺序执行
try块后,进入finally块。
12.9.3 补充说明¶
-
如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用
finally {} -
可以有多个 catch 语句(可以无 catch),捕获不同的异常(进行不同的业务处理),
- 要求父类异常在后,子类异常在前,比如(
Exception在后,NullPointerException在前), - 如果发生异常,只会匹配一个
catch - 从小分类到大分类
- 要求父类异常在后,子类异常在前,比如(
-
可以进行
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 处理流程¶
- 方法通过
throws声明可能抛出的异常。 - 调用者必须使用
try-catch或继续throws处理这些异常。 - 如果异常未被处理,可能导致程序终止。
12.10.3 补充说明¶
-
在方法声明中用
throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。 -
对于编译异常,程序中必须处理,比如
try-catch或者throws -
对于运行时异常,程序中如果没有处理,默认就是
throws的方式处理 -
子类重写父类的方法时,确出异常的规定:子类重写的方法所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类
-
在
throws过程中,如果有方法try-catch, 就相当于处理异常,就可以不必throws -
如果抛出的时运行时异常,则不用处理,因为 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 自定义异常的步骤¶
- 定义类:自定义异常类名(程序员自己写)继承 Exception 或 RuntimeException
- 如果继承Exception, 属于编译异常
- 如果继承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. throw 和 throws 的区别]]