Java异常处理
异常的介绍和体系结构
异常介绍
- 指的是程序在编译或执行过程中,出现的非正常的情况(错误)
- ArrayIndexOutOfBoundsException:索引越界异常
- ClassCaseException:类型转换异常
- NullPointerException:空指针异常
- 注意:语法错误,不是异常
异常体系
Java中所有的异常和错误的父类是Throwable,它的两个子类为Error和Exception
Error:
- 严重级别问题
- 常见的有栈内存溢出(StackOverflowError),堆内存溢出(OutOfMemoryError)
Exception:
- RuntimeException及其子类:运行时异常
- 数组索引越界异常、空指针异常、类型转换异常都属于运行时异常
- 编译阶段没有错误,运行时可能会出现错误。这种错误通常都是程序员代码不严谨造成的
- 除RuntimeException之外的所有异常:编译时异常
- 编译阶段就出现的错误。主要起到提醒作用
异常的默认处理流程
示例:
1 | package com.norlcyan.exception; |
处理流程图:
sequenceDiagram
participant JVM
participant Main as main方法
participant Method as method方法
JVM->>Main: 程序开始执行
Main->>Main: 打印 "main start..."
Main->>Method: 调用method()方法
Method->>Method: 打印 "method start..."
Method->>Method: 执行 int i = 1/0
Note right of Method: 发生算术异常
Method-->>Method: 创建ArithmeticException对象
Method-->>Main: 抛出异常对象
Note right of Main: 接收到异常
Main-->>JVM: 继续向上抛出异常
Note right of JVM: JVM接收到异常
JVM->>JVM: 打印异常信息
JVM->>JVM: 终止程序执行
异常处理方式
try…catch
能够抛出的异常对象捕获,然后执行异常的处理方案
好处:程序不会直接停止,而是执行异常的处理方案、
格式:
1
2
3
4
5try {
可能会出现异常的代码
} catch (异常类型1 变量) {
处理异常的方案
}示例:
1
2
3
4
5
6
7
8
9
10
11
12
13public class ExceptionDemo02 {
public static void main(String[] args) {
System.out.println("开始");
try {
int i = 1 / 0;
} catch (ArithmeticException e) {
System.out.println("[" + e + "] 捕获了异常...");
System.out.println("异常原因:" + e.getMessage());
e.printStackTrace(); // 输出详细异常信息,不会中断程序执行
}
System.out.println("结束");
}
}
异常的所有方法都在父类Throwable中实现了,并且只需要记住两个方法
getMessage:获取到发生异常的原因
printStackTrace:输出详细异常信息,不会中断程序异常
当异常对象不匹配catch中的类型时,是不会执行异常处理方案的。当需要捕获多种异常时,可以创建多个catch语句或使用异常类型分隔符:
1 | try { |
当使用异常类型分隔符时,最终编译的字节码文件中会转换为多个catch
如果异常类型为Exception(所有异常的父类),那么要在最后catch,使用异常类型分隔符也必须只能是兄弟类型
throws抛出
- throws:用在方法上,作用是声明,声明这个方法中的异常是抛出处理
- 格式:
public void method() throws 异常1, 异常2, 异常3 ... {}
示例:
1 | import java.io.FileNotFoundException; |
当某个方法抛出了异常,那调用这个方法的方法也需要抛出异常,且类型大小不能低于(>=)被调用方法的异常
当某个子类中重写后的成员方法抛出了异常,那它的父类也方法也需要抛出异常,且子类异常类型不能大于(<=)父类异常
throw关键字
这是用于抛出异常的关键字,使用方式就是throw new Exception(...),理解为抛出创建的异常对象
比如,当创建学生对象时,需要对年龄进行负数判断:
1 | public void setAge(int age) throws Exception { |
try…catch…和throws
使用思路:当问题需要暴露给开发者时,优先使用throws。需要将问题捕获,且不影响程序运行,就选择try…catch
自定义异常
创建自定义异常,可以更加精准的捕获异常
自定义异常分类:
- 自定义编译时异常:
- 定义一个异常类继承
Exception - 重写构造器
- 定义一个异常类继承
- 自定义运行时异常:
- 定义一个异常类继承
RuntimeException - 重写构造方法
- 定义一个异常类继承
示例:
1 | public class StudentAgeException extends Exception{ |
抛出异常
1 | public void setAge(int age) throws StudentAgeException { |
捕获异常
1 | try { |
编译时异常(检查异常)- 必须声明throws
运行时异常(非检查异常)- 不需要声明throws
自定义异常的选择
| 场景 | 异常类型 | 是否需要throws |
|---|---|---|
| 业务逻辑异常 | Exception | ✅ 必须声明 |
| 可恢复异常 | Exception | ✅ 必须声明 |
| 外部资源异常 | Exception | ✅ 必须声明 |
| 编程错误 | RuntimeException | ❌ 不需要声明 |
| 系统错误 | RuntimeException | ❌ 不需要声明 |
| 违反契约 | RuntimeException | ❌ 不需要声明 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Norlcyan's Blog!
