整理自 Java 异常面试题 33 道。详细原理见 Java 异常处理、final、finally、finalize。
相关学习笔记
| 专题 | 笔记 |
|---|---|
| 异常处理 | Java 异常处理 |
| final/finally/finalize | final、finally、finalize 区别 |
目录
异常体系
1. Java 异常简介
异常是 Java 识别并响应错误的统一机制,让业务代码与错误处理分离。好的异常能回答:什么(类型)、在哪(堆栈)、为什么(消息)。
2. Throwable 体系
Throwable 是根类,含 Error 与 Exception 两大子类。
Error:JVM 级严重错误(OutOfMemoryError、StackOverflowError),应用一般不捕获,也不应自定义 Error 子类。
Exception:程序可捕获处理。
- RuntimeException(非受检):空指针、数组越界、类型转换、算术异常等;编译器不强制处理,应通过改代码避免
- 其他 Exception(受检):
IOException、ClassNotFoundException等;必须try-catch或throws
3. 受检异常与非受检异常
| 受检(checked) | 非受检(unchecked) | |
|---|---|---|
| 范围 | Exception 除 RuntimeException 及其子类 | RuntimeException + Error |
| 编译器 | 必须处理或声明 | 不强制 |
| 语义 | 可预期的、调用者应处理的失败 | 编程错误或未检查的运行时问题 |
4. 异常关键字
try 监听;catch 捕获;finally 收尾(常释放资源);throw 方法内抛出;throws 方法签名声明可能抛出的异常。
处理机制
5. 声明异常(throws)
对知道如何处理的异常可捕获;不知道的继续向上 throws。受检异常必须处理或声明;Error、RuntimeException 不必用 throws 声明。
6. 抛出异常(throw)
在方法内 throw new XxxException(),终止当前方法执行,交给调用栈处理。
7. 捕获异常(try-catch)
运行期未知错误可在当前层 try-catch 处理,避免直接抛到最外层。
8. 如何选择异常类型
能预检查规避的用 if(如 NPE);必须解析字符串数字才 catch NumberFormatException;业务失败用有语义的自定义异常。
9. 直接抛出 / 封装再抛出 / 捕获 / 自定义异常
- 不会处理 →
throws向上 - 换异常类型对外暴露 →
catch后throw new MyException(..., e)保留cause - 区分类型 → 多个
catch或catch (A | B e) - 自定义 → 通常提供无参 + 带 message 构造
10. try-catch-finally 与 try-with-resources
finally 一般总会执行(用于关资源)。try-with-resources(Java 7+)自动关闭 AutoCloseable,若关闭时也抛异常,会抑制为 suppressed 异常。
11. Error 和 Exception 区别
Error:虚拟机级,应用一般不捕获。Exception:应用可捕获处理,程序可继续或优雅失败。
12. 运行时异常和受检异常区别
编译器是否强制处理;本质:是否强制调用者必须处理——强制则用受检,否则常用 RuntimeException。
13. JVM 如何处理异常
方法抛异常 → 创建异常对象(含堆栈)→ 沿调用栈向上找处理器 → 找到则执行 catch → 找不到则由默认处理器处理并终止线程(打印堆栈)。
14. throw 和 throws 区别
| throw | throws | |
|---|---|---|
| 位置 | 方法体内 | 方法签名 |
| 数量 | 一次抛一个 | 可声明多个 |
| 类型 | 受检/非受检均可 | 声明可能抛出的类型 |
15. final、finally、finalize 区别
- final:类不可继承、方法不可重写、变量常量
- finally:异常处理中必定执行的收尾块
- finalize:Object 方法,GC 前清理(已不推荐依赖)
16. NoClassDefFoundError 和 ClassNotFoundException 区别
| NoClassDefFoundError | ClassNotFoundException | |
|---|---|---|
| 类型 | Error | 受检 Exception |
| 时机 | 运行时找不到类定义(编译时有,运行缺失) | 加载时找不到类(Class.forName 等) |
| 处理 | 不应捕获 | 应捕获或声明 |
17. try-catch-finally 中哪个部分可以省略
catch 可省略(仅 try-finally)。但仅 try 不能处理受检异常(编译不通过);try 适合只处理运行时异常或已声明 throws 的场景。
18. catch 中 return 了,finally 还会执行吗
会,在 return 之前执行 finally。注意:finally 中的 return 会覆盖 try/catch 的返回值;修改基本类型返回值在 finally 中往往无效(return 路径已绑定常量)。不要在 finally 中 return。
19. ExampleA / ExampleB 捕获顺序
ExampleB extends ExampleA,throw new ExampleB() 被 catch (ExampleA e) 捕获,输出 ExampleA(多态,子类对象可被父类 catch 接住)。
20. 《Java 编程思想》异常题输出
Caught AnnoyanceCaught SneezeHello World!内层 catch Annoyance 后重新抛出,外层 catch Sneeze,finally 仍执行。
21. 常见 RuntimeException
NullPointerException、ClassCastException、ArrayIndexOutOfBoundsException、IndexOutOfBoundsException、ArithmeticException、NumberFormatException、IllegalArgumentException 等。
22. Java 常见异常(节选)
IllegalAccessError、InstantiationError、OutOfMemoryError、StackOverflowError、ClassCastException、ClassNotFoundException、ArithmeticException、ArrayIndexOutOfBoundsException、NullPointerException、NoSuchMethodException 等。
最佳实践与规约
23. 在 finally 或 try-with-resources 中关闭资源
不要在 try 末尾关流(异常时可能执行不到);用 finally 或 try-with-resources。
24. 优先明确的异常类型
抛 NumberFormatException 而非笼统的 IllegalArgumentException;API 文档用 @throws 说明。
25. 使用描述性消息
异常 message 简洁说明上下文,便于日志与排查。
26. 优先捕获最具体的异常
catch (NumberFormatException) 放在 catch (IllegalArgumentException) 之前,否则子类块不可达。
27. 不要捕获 Throwable
会连 Error 一起捕获,可能掩盖 OOM、StackOverflow 等严重问题。
28. 不要忽略异常
空 catch 吞异常;至少打日志或重新抛出。
29. 不要记录后又抛出同一异常
易重复日志;要么处理,要么抛;包装时用自定义异常并保留 cause。
30. 包装异常不要丢掉原始异常
new MyException(msg, e) 或 initCause(e),保留堆栈。
31. 不要用异常控制流程
异常比 if 慢,应用条件分支处理正常分支。
32. 优先使用标准异常
能满足需求就用 JDK 已有异常,再考虑自定义 XxxException。
33. 阿里巴巴 Java 开发手册(异常节选)
- NPE、数组越界等应用 if 预检查,不要靠 catch
- 异常不做流程控制;区分稳定/非稳定代码
- 捕获就要处理,最外层转化为用户可理解提示
finally关闭资源;禁止 finally 中 return- 捕获类型与抛出类型匹配;RPC/反射场景可用
Throwable拦截NoSuchMethodError等 - 推荐业务自定义异常,避免裸抛
Exception - 对外 API 用错误码,内部可用异常;跨应用 RPC 可考虑
Result封装