文章目录
一、异常简介
就是程序出现了不正常的情况。
异常结构:
- Throwable: 所有错误和异常的超类。
- Error: 严重问题,不需要处理。
- Exception: 异常类,表示程序本身可以处理的问题。
- Checked Exception: 编译期不检查,出现问题后,需要回来修改代码。
- Unchecked Exception: 编译期必须处理,否则程序不能通过编译、不能运行。
二、异常体系
2.1、Throwable 类
Throwable 类位于 java.lang 包下,它是 Java 语言中所有错误(Error)和异常(Exception)的父类。
Throwable 类包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。
成员方法:
方法名 | 说明 |
---|---|
public Throwable fillInStackTrace() | 填写执行堆栈跟踪 |
public StackTraceElement[] getStackTrace() | 提供对 printStackTrace() 打印的堆栈跟踪信息的编程访问 |
public Throwable getCause() | 如果原因不存在或未知,则返回此 throwable 的原因或 null |
public String toString() | 返回此可抛出的简短描述 |
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public void printStackTrace() | 把 throwable 对象封装的异常错误信息输出在控制台 |
2.2、Error 类
Error 是 Throwable 的一个直接子类,它指示合理的应用程序不应该尝试捕获的严重问题。
这些错误在应用程序的控制和处理能力之外,编译器不会检查 Error。
对于设计合理的应用程序来说,即使发生了错误,本质上也无法通过异常处理来解决其所引起的异常状况。
常见 Error :
- OutOfMemoryError:内存溢出错误;
- UnsupportedClassVersionError:Java 类版本错误;
- …
2.3、Exception 类
Exception 是 Throwable 的一个直接子类,它指示合理的应用程序可能希望捕获的条件。
Exception 又包括 Checked Exception(检查异常)和 Unchecked Exception(非检查异常)两大类别。
2.3.1、Checked Exception
Checked Exception 是编译器要求必须处理的异常,除了 RuntimeException 以及它的子类,都是 Checked Exception 异常。
我们程序编写时就必须处理此类异常,否则程序无法编译通过。
常见检查异常:
- IOException:IO 异常;
- SQLException:SQL 异常;
- …
2.3.2、Unchecked Exception
Unchecked Exception 是编译器不要求强制处理的异常,包含 RuntimeException 以及它的相关子类。
我们编写代码时即使不去处理此类异常,程序还是会编译通过。
常见非检查异常:
- NullPointerException:空指针异常;
- ArrayIndexOutOfBoundsException:数组下标越界异常;
- …
三、JVM 默认处理方案
若程序出现问题,我们没有做任何处理,最终 JVM 会做默认的处理。
-
把异常的名称,异常原因及异常出现的位置等信息输出在控制台。
-
程序停止执行。
示例:数组下标越界。
// 当数组下标越界,程序在编译阶段不会发生错误,但在运行时会抛出异常
public class Test_01 {
public static void main(String[] args) {
System.out.println("程序开始");
int[] arr = {1, 2, 3};
System.out.println("arr = " + arr[3]); // 程序在这里发生异常,提前结束运行
System.out.println("程序结束"); // 这行代码没有执行
}
}
运行:
程序开始
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at exception.ExceptionDemo01.main(ExceptionDemo01.java:17)
异常原因:由于访问了数组中不存在的元素,所以代码停止执行并打印了相关的异常信息,此信息为堆栈跟踪。
异常信息:
- Exception in thread “main”(异常位于主线程 main 中)
- java.lang.ArrayIndexOutOfBoundsException(异常类型)
- 3(异常详细信息)
- at exception.ExceptionDemo01.main(Test_01.java:17)(异常发生位置)
四、异常处理
若程序出现问题,我们需要自己来处理,在 Java 语言中,异常处理机制分为两部分。
- 抛出异常(throws)
- 当一个方法发生错误时,会创建一个异常对象,并交给运行时系统处理。
- 当前方法不处理,而是声明抛出,由该方法的调用者来处理。
- 捕获异常(try … catch …)
- 在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器。
- 在当前方法中使用 try-catch 的语句块来处理异常。
Java 通过 5 个关键字来实现异常处理,分别是:throw
、throws
、try
、catch
、finally
,异常总是先抛出后捕获的。
五、捕获异常
格式:
try {
// 可能产生异常的代码
} catch (异常类名 变量名) {
// 捕获并处理try抛出的异常类型,记录日志|打印异常信息|继续抛出异常
} catch (异常类名N 变量名) {
// 捕获并处理try抛出的异常类型,记录日志|打印异常信息|继续抛出异常
} finally {
// 无论是否发生异常,都将执行的代码块
}
流程:
① 程序从 try 里面的代码开始执行。
② 出现异常,会自动生成一个异常类对象,该异常对象将被提交给 Java 运行时系统。
③ 当 Java 运行时系统接收到异常对象时,会到 catch 中去找匹配的异常类,找到后进行异常的处理。
④ 处理异常执行完毕之后,程序还可以继续往下执行。
⑤ 无论程序是否发生异常,finally 中代码块都会执行。
示例:
public class Test_02 {
public static void main(String[] args) {
System.out.println("程序开始");
int[] arr = {1, 2, 3};
try {
System.out.println("arr = " + arr[3]); // 可能发生异常的代码
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace(); // 处理异常(打印异常信息)
} finally {
System.out.println("无论是否发生异常,都将执行的代码块");
}
System.out.println("程序结束");
}
}
运行:
程序开始
无论是否发生异常,都将执行的代码块
程序结束
java.lang.ArrayIndexOutOfBoundsException: 3
at step_05_exception.Test_02.main(Test_02.java:8)
将第 6 行代码 arr[3] 改为 arr[2] 再运行:
程序开始
arr = 3
无论是否发生异常,都将执行的代码块
程序结束
六、抛出异常
6.1、throw
用在方法体内,跟的是异常对象名,表示抛出异常,由方法体内的语句处理。
执行 throw 一定抛出了某种异常。
示例:
public class Test_03 {
public static void main(String[] args) {
System.out.println("程序开始");
divide(2, 0); // 调用除法
System.out.println("程序结束");
}
// 除法方法
public static void divide(int a, int b) {
System.out.println("除法开始");
if (b == 0) {
throw new ArithmeticException("除数不能为零"); // 抛出异常、但没有处理异常、异常发生时程序中断不再往下执行
}
System.out.println(a / b);
System.out.println("除法结束");
}
}
运行:
程序开始
除法开始
Exception in thread "main" java.lang.ArithmeticException: 除数不能为零
at step_05_exception.Test_03.divide(Test_03.java:15)
at step_05_exception.Test_03.main(Test_03.java:7)
6.2、throws
用在方法声明后面,跟的是异常类名,表示抛出异常,由该方法的调用者来处理。
表示出现异常的一种可能性,并不一定会发生这些异常。
格式:
修饰符 返回值类型 方法名 (参数列表) throws 异常类名1, 异常类名2, 异常类名3... {
//方法体
}
示例:
public class Test_04 {
public static void main(String[] args) {
System.out.println("程序开始");
try {
divide(2, 0); // 调用除法
} catch (ArithmeticException e) {
e.printStackTrace(); // 处理异常(打印异常信息)
}
System.out.println("程序结束");
}
// 抛出异常,不在方法中处理,谁调用该方法谁处理
public static void divide(int a, int b) throws ArithmeticException {
System.out.println("除法开始");
System.out.println(a / b);
System.out.println("除法结束");
}
}
运行:
程序开始
除法开始
程序结束
java.lang.ArithmeticException: / by zero
at step_05_exception.Test_04.divide(Test_04.java:19)
at step_05_exception.Test_04.main(Test_04.java:8)
七、自定义异常
示例:
public class Test_05 {
public static void main(String[] args) {
System.out.println("程序开始");
checkAge(200); // 输入0~120范围之外的年龄
System.out.println("程序结束");
}
// 检查年龄方法
static void checkAge(int age) {
System.out.println("开始检查");
if (age < 0 || age > 120) {
throw new ageException("年龄应在0~120之间");
} else {
System.out.println("年龄合法:" + age + "岁");
}
System.out.println("结束检查");
}
// 自定义年龄异常,继承运行时异常
static class ageException extends RuntimeException {
public ageException() {
}
public ageException(String message) {
super(message);
}
}
}
运行:
程序开始
开始检查
Exception in thread "main" step_05_exception.Test_05$ageException: 年龄应在0~120之间
at step_05_exception.Test_05.checkAge(Test_05.java:14)
at step_05_exception.Test_05.main(Test_05.java:6)
第 6 行代码 checkAge() 方法中输入0~120范围之内的年龄再运行:
程序开始
开始检查
年龄合法:18岁
结束检查
程序结束