Throwable是Java异常体系的唯一根类,其下仅有Error和Exception两个直接子类;Error表示JVM级不可恢复错误,Exception表示可处理的程序异常,其中RuntimeException为unchecked异常,IOException等为checked异常。

Throwable 是所有异常和错误的唯一根类
Java 中能被 throw 或 catch 的,只有 Throwable 及其子类。不是继承自它的对象,连抛出都不合法——编译器直接报错 incompatible types: xxx cannot be thrown。
它有两个直接子类:Error 和 Exception,没有第三个。这点常被初学者误画成“三层结构”(比如把 RuntimeException 当作和它们并列),其实 RuntimeException 是 Exception 的子类,而 Exception 本身是 Throwable 的子类。
-
Error:JVM 层面崩溃级问题,比如OutOfMemoryError、StackOverflowError,程序不该捕获,也几乎无法恢复 -
Exception:程序逻辑或外部交互中可预期、可干预的问题,才是你日常要处理的对象
运行时异常(RuntimeException)为什么不用 try-catch 也能编译通过
因为它们属于 unchecked exception,编译器不强制检查。这不是设计疏忽,而是明确告诉你:“这类异常本该靠写得更严谨来避免,而不是靠兜底。”
比如 NullPointerException,真正该做的是在调用前加 if (obj != null),而不是等它炸了再 catch;ArrayIndexOutOfBoundsException 应该靠 list.size() > i 防御,而不是靠捕获后打印日志。
- 常见运行时异常:
NullPointerException、IllegalArgumentException、ClassCastException、ArithmeticException - 它们都继承自
RuntimeException,而RuntimeException继承自Exception - 如果真去
catch (Exception e),反而会掩盖真正的逻辑缺陷——你本该修复空指针,而不是吞掉它
为什么 IOException 必须 try-catch 或 throws,否则编译失败
因为它是 checked exception,编译器强制你面对现实:文件可能不存在、磁盘可能满、网络可能断。这些不是代码写错了,而是外部环境不可控,你必须显式声明“我处理了”或“我甩给上层”。
这种机制逼你思考失败路径,而不是假装一切顺利。但代价是代码变啰嗦——这也是 Java 后续版本(如 NIO.2 的 Files 工具类)悄悄提供返回 Optional 或抛运行时异常的替代 API 的原因。
- 典型 checked 异常:
IOException、SQLException、ClassNotFoundException - 不能只写
catch (Exception e)来一锅端——这会让编译器认为你“已处理”,但实际掩盖了其他未声明的 checked 异常 - 若方法内抛出多个 checked 异常,
throws必须列出全部,顺序无关,但缺一不可
别 catch Error,也别 throw new Error("xxx")
Error 不是给业务代码用的。你手动 new 一个 Error 抛出去,等于告诉 JVM:“我现在要模拟一次虚拟机崩溃”。生产环境里没人会这么干。
更常见的误区是捕获 Exception 时,无意中也吞掉了 Error 子类(因为 Error 也是 Throwable)。虽然语法上允许,但结果往往是:OOM 发生了,你的 catch (Exception e) 完全无感,程序静默卡死。
- 正确做法:顶层用
Thread.setDefaultUncaughtExceptionHandler捕获未处理的Throwable,但只做日志+告警,绝不尝试“恢复” -
NoClassDefFoundError和ClassNotFoundException容易混淆:前者是运行时类加载失败(比如依赖 jar 被删),后者是反射时找类失败(比如Class.forName("xxx")找不到),前者是Error,后者是Exception - 永远不要在业务逻辑里
throw new Error()—— 你要表达的是“非法参数”,就该用IllegalArgumentException
最常被忽略的一点:异常链(getCause())在嵌套调用中极易断裂。比如 A 方法 catch 了 IOException,又 throw new RuntimeException("xxx"),原始异常信息就丢了。要用 new RuntimeException("xxx", e) 显式保留 cause。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











