JavaSE:异常
异常:程序在运行过程中出现的特殊情况。
异常是一个事件,干扰程序的正常执行。
1、异常体系
1.1、API 层次
java.lang.Throwable:所有异常和错误的父类。包含线程执行堆栈的快照,提供
printStackTrace()等 API 用于获取堆栈信息。
-
Error:JVM、硬件资源等错误,无法手动处理。
- 示例:StackOverFlowError、OOM、NoClassDefFoundError
-
Exception:程序编译和运行时产生的问题,可处理。
-
运行时异常:RuntimeException 及其子类。如空指针、下标越界、类型转换异常...
-
编译时异常:RuntimeException 以外的异常。如 IO、SQL 异常。

-
1.2、异常类型
Java 异常分为两类:非受检异常(不可查)
-
非受检异常(unchecked)
- 包含运行时异常(Exception 及其子类)、错误(Error)。
- 不要求强制处理的异常,即可处理或不处理。
-
受检异常(checked)
- 包含 Exception 中的非运行时异常。
- Java 编译器会检查此类异常,要求必须处理的异常(捕获 or 抛出)。
2、异常使用
2.1、关键字
| 作用 | 含义 | |
|---|---|---|
| try | 监听 | 将可能抛出异常的代码放在 try 块中 |
| catch | 捕获 | 捕获 try 块中的异常 |
| finally | 最终执行 | 即使发生异常也会执行,通常用于释放资源 |
| throw | 抛出 | 手动抛出异常 |
| throws | 声明方法异常 | 在方法签名中声明此方法可能抛出的异常 |
2.2、异常产生
- 系统产生:程序运行时遇到不合规范的代码或结果时,自动产生异常。
- 手动抛出:使用
throw关键字手动抛出异常。- 通常不需要手动抛出异常,而是将系统产生的异常捕获或向上抛出。
- 可以在 catch 中抛出异常,目的是改变异常类型。
2.3、异常处理
说明
- 受检异常必须处理,非受检异常可不处理。
- 异常的传递:按方法调用链反向传递。
- 若始终向上抛出,最终由 JVM 进行默认异常处理。
- 即打印堆栈跟踪信息(
e.printStackTrace()),并终止程序。
处理方式
- 捕获:使用
try-catch块捕获方法异常。 - 向上抛出:在方法签名中使用
throws关键字,将异常抛给调用者。
异常捕获
- try-catch
- try-catch-finally
- try-finally
- try-with-resource
① t-c
single-catch
单个 catch 块仅捕获单个异常类型
-
单个 catch:捕获 try 块中唯一可能发生的异常。
-
多个 catch:捕获 try 块中多个可能发生的异常。
-
按 catch 块的声明顺序先后匹配异常。
-
顺序要求:优先定义最具体的异常类型(先子后父,先小后大)。
try { // ... } catch (FileNotFoundException e) { // ... } catch (IOException e) { // ... }
-
multi-catch(?)
单个 catch 捕获多个异常类型,用
|隔开(JDK7 引入)
-
JVM 会检测 try 块中的异常()
-
catch 块中的异常类型没有顺序要求,但不能同时存在子类和父类异常类型,否则编译不通过。
try { // ... } catch (FileNotFoundException | NullPointerException e) { // ... }
② t-c-f
- 没有异常
- 执行完 try 块。
- 跳过 catch。
- 执行 finally。
- 跳出 t-c-f。
- 捕获到异常
- catch 块中没有定义相应类型的异常
- 执行到 try 块的第 k 行,发生异常。
- JVM 进行默认异常处理:打印堆栈信息、终止程序。
- catch 捕获到相应类型的异常
- 执行到 try 块的第 k 行,发生异常。
- try 中第 k 行之后的代码不会执行,跳到相应的 catch 块。
- 执行完 catch 块,执行 finally 块。
- 跳出 t-c-f。
- catch 块中没有定义相应类型的异常
③ t-f
场景:不需要捕获异常,仅用于释放连接。
示例
- 使用 时释放资源。
- 并发编程中 的使用和释放。
- 的释放。
④ t-w-r(?)
JDK7 引入:
-
作用:
-
自动释放资源,可不写 finally 块。
-
避免覆盖 try 块中的异常类型,保留所有可能出现的异常。
-
-
使用前提:资源实现了 AutoCloseable 接口。
- 常见接口:InputStream、OutputStream、Connection、Statement、ResultSet 等。
示例
-
代码:在 try 块中创建资源对象。
try(InputStream is = new FileInputStream("d:\\1.txt")) { System.out.println(is); } catch (IOException e) { e.printStackTrace(); } -
等价于:在 try 块中创建资源对象,并再次使用 try-catch 语句释放资源。
try { InputStream is = new FileInputStream("d:\\1.txt"); // 可能出现的异常 Throwable t = null; try { System.out.println(is); } catch (Throwable e1) { t = e1; throw e1; } finally { // 资源非空,需要释放 if (is != null) { // 内部try块发生异常 if (t != null) { try { is.close(); } catch (Throwable e2) { // 资源释放出现异常,作为被压制异常添加 t.addSuppressed(e2); } } else { is.close(); } } } } catch (IOException e) { e.printStackTrace(); }
2.4、自定义异常
-
继承 Expectation 超类。
-
提供 2 个构造方法
-
无参
-
msg:用于打印异常信息,调试。
public class MyException extends Exception { public MyException(){} public MyException(String msg){ super(msg); } // ... }
-
3、说明
3.1、finally 特殊情况(?)
3.2.1、不被执行
以下情况,finally 不会正常执行。
- 执行 finally 块时发生异常,异常之后的代码不会被执行。
- 程序所在线程死亡。
- 执行 finally 块之前 JVM 终止(
System.exit()) - CPU 关闭。
3.2.2、return
若在 finally 块中使用 return
-
异常吞噬:try 块抛出异常时会跳到 finally 块,而执行 finally 块的 return 时直接返回,没有对异常进行处理。
public boolean foo() { try { // 抛出异常 } finally { // ... return true; } } -
对方法返回值的影响:try 块会固定作为返回值的变量,finally 块中对该变量进行修改时不会影响返回值。
// 以下程序返回 10 public int foo() { int result; try { result = 10; return result; } finally { result = 20; return result; } }
结论:不要在 finally 块中使用 return 关键字。
3.2、异常总结
结合 深入理解。
- 异常体系:
- API:Error、Exception(运行时、非运行时)
- 受检异常、非受检异常
- 关键字:try、catch、finally、throw、throws
- try、catch、finally 不能单独使用。
- 捕获:t-c、t-c-f、t-f、t-w-r
- 异常产生:系统产生、throw 产生。
- 异常处理:向上抛出、捕获。
- 自定义异常