关键在于设计意图、可恢复性与处理责任:exception代表可预期、可干预、可恢复的业务异常,需显式处理;error代表jvm严重故障,不可恢复,禁止常规捕获。
区分编译时异常(更准确说是“编译期检查的异常”)中 exception 和 error,关键不在“编译时发生”,而在于**设计意图、可恢复性与处理责任**。java 编译器只对部分 exception(即 checked exception)做语法检查,而 error 无论何时都不会被编译器强制要求处理——它压根就不该出现在正常业务逻辑里。
看继承关系和语义定位
两者都直接继承自 Throwable,但分属不同分支:
- Exception:代表“程序运行中可预期、可干预、可恢复的异常情况”。比如文件不存在、数据库连接超时、用户输入格式错误——这些是业务流程中需要响应的分支,不是崩溃信号。
- Error:代表“JVM 自身遭遇严重故障,程序已失去运行基础”。比如 OutOfMemoryError(堆内存彻底耗尽)、StackOverflowError(调用栈爆满)、NoClassDefFoundError(类加载链断裂)。它们不是 bug,而是系统级失稳。
看编译器是否强制你管
编译器只对 checked Exception(如 IOException、SQLException)施加约束:
- 不 try-catch,也不在方法签名写 throws → 编译直接失败。
- Error 和 RuntimeException 及其子类(如 NullPointerException、IllegalArgumentException)——编译器完全放行,不管有没有处理。
看能不能/该不该 try-catch
行为准则很明确:
- 对 Exception(尤其是 checked 类型):鼓励捕获并做有意义处理,比如提示用户重试、切换备用资源、记录告警后降级返回。
- 对 Error:禁止常规捕获。即使写了
catch (OutOfMemoryError e),你也几乎无法安全地“恢复”——JVM 状态可能已损坏。真要处理,只应在极少数监控场景中全局捕获,仅用于日志记录或触发紧急告警,随后应让进程退出。
看自定义时该继承谁
你自己抛的异常,选父类就是选契约:
- 继承 Exception → 告诉调用方:“这事可能发生,你得负责应对”,适用于外部依赖失败、业务规则拒绝等可预期场景(如 InsufficientBalanceException)。
- 继承 RuntimeException → 告诉调用方:“这是你的逻辑错,快去修代码”,适用于空指针、状态非法、配置缺失等本不该发生的内部问题。
- 永远不要继承 Error —— 你不是在写 JVM,也不是在模拟系统崩溃。











