从 TypeScript 到 Java(8):异常处理与错误机制 —— 让程序“有计划地失败”
·
从 TypeScript 到 Java(8):异常处理与错误机制 —— 让程序“有计划地失败”
系列导读:
在 TypeScript 中,我们几乎可以随意throw一个错误对象,也不一定非要try...catch。但在 Java 中,异常是一等公民:
它被严格分类、必须声明、还可能强制处理。这套体系让 Java 程序更安全、更可预测,也更“唠叨”。
☕ 一句话理解
Java 的异常系统是一种“受控错误传播机制”:
程序在运行中如果遇到问题,不是立刻崩溃,而是通过 Exception 类体系 有序地传递、捕获与恢复。
⚙️ Java 异常的三层结构
Java 的异常都继承自顶层类 Throwable:
Throwable
├── Error(严重错误,不建议捕获)
└── Exception
├── Checked Exception(必须处理)
└── RuntimeException(可选处理)
📘 简图:
Throwable
├── Exception
│ ├── IOException
│ ├── SQLException
│ ├── ClassNotFoundException
│ └── ...
│
└── RuntimeException
├── NullPointerException
├── IndexOutOfBoundsException
├── IllegalArgumentException
└── ...
核心理念:
Error:系统级崩溃(如内存溢出),不要处理。Checked Exception:编译器强制处理(必须 try 或 throws)。RuntimeException:程序逻辑错误,可选择性处理。
🧩 最基础的异常语法
try {
int x = 10 / 0; // 抛出 ArithmeticException
} catch (ArithmeticException e) {
System.out.println("不能除以0");
} finally {
System.out.println("执行清理工作");
}
输出:
不能除以0
执行清理工作
关键点:
try:放可能出错的代码;catch:捕获异常;finally:无论是否出错都执行(常用于释放资源)。
🧠 Checked 与 Unchecked 异常区别
Checked Exception(受检异常)
必须显式处理,否则编译报错。
import java.io.FileReader;
import java.io.IOException;
public class Demo {
public static void main(String[] args) throws IOException {
FileReader reader = new FileReader("notfound.txt");
}
}
编译器报错:
error: unreported exception IOException; must be caught or declared to be thrown
解决办法:
- 用
try...catch捕获; - 或在方法签名中加
throws IOException。
public static void main(String[] args) {
try {
FileReader reader = new FileReader("notfound.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
Unchecked Exception(非受检异常)
无需强制捕获。
int[] nums = {1, 2, 3};
System.out.println(nums[3]); // 抛出 IndexOutOfBoundsException
这类异常通常代表程序逻辑错误,
不需要编译器干预,由开发者自行修复。
🔧 throws 与 throw 的区别
| 关键字 | 用途 | 位置 |
|---|---|---|
throw |
实际抛出异常对象 | 方法体内 |
throws |
声明该方法可能抛出的异常 | 方法签名 |
示例:
public static void risky() throws IOException {
throw new IOException("文件读取失败");
}
调用时:
try {
risky();
} catch (IOException e) {
System.out.println(e.getMessage());
}
输出:
文件读取失败
🧱 自定义异常类
如果想创建自定义业务异常,可以继承 Exception 或 RuntimeException。
class UserNotFoundException extends Exception {
public UserNotFoundException(String message) {
super(message);
}
}
使用:
public void findUser(String id) throws UserNotFoundException {
if (id == null) {
throw new UserNotFoundException("用户ID不能为空");
}
}
💡 建议:
- 继承
Exception→ 强制调用者处理;- 继承
RuntimeException→ 可选处理(常用于业务校验)。
📘 异常传播机制
当异常未被捕获时,会沿着调用栈向上传递:
public static void main(String[] args) {
methodA();
}
static void methodA() {
methodB();
}
static void methodB() {
int x = 1 / 0; // ArithmeticException 向上传递
}
最终由 JVM 捕获并打印堆栈信息。
输出示例:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Demo.methodB(Demo.java:10)
at Demo.methodA(Demo.java:6)
at Demo.main(Demo.java:2)
🔍 与 TypeScript 对比
| 功能 | TypeScript | Java |
|---|---|---|
| 错误类型 | 任意对象 | 必须继承自 Throwable |
| 异常声明 | 无需声明 | Checked Exception 必须声明 |
| 捕获机制 | try-catch-finally(可选) | try-catch-finally(部分强制) |
| 异常分类 | 无 | 三层结构:Error / Exception / RuntimeException |
| 编译期检查 | 无 | 有 |
TypeScript 示例:
function readFile() {
throw new Error("File not found");
}
try {
readFile();
} catch (e) {
console.error(e.message);
}
TypeScript 的错误完全是“运行时行为”;
Java 的异常同时是“语言语义 + 编译约束”。
🧩 最佳实践建议
✅ 1. 不滥用异常:
- 异常不是控制流;
- 不要用异常代替
if判断。
✅ 2. 分层处理:
- 底层抛出,顶层捕获;
- 保留堆栈信息,方便排查。
✅ 3. 记录日志:
- 使用
Logger而不是System.out.println; - 结合
e.printStackTrace()输出完整信息。
✅ 4. 自定义异常要语义清晰:
- 如
InvalidOrderException、UserNotFoundException。
🧭 哲学层面理解
- TypeScript 追求“开发便利”:错误出现时,更多依赖调试;
- Java 追求“系统稳定”:编译期即要求程序员思考“错误可能发生的地方”;
- Checked Exception 是一种“强制责任”的设计——提醒开发者对每一步承担后果。
☕ 这也正是企业级 Java 项目少见“莫名其妙崩溃”的原因。
🚀 实战练习
编写一个方法 readUserFile(path):
- 若文件不存在 → 抛出
FileNotFoundException; - 若内容为空 → 抛出自定义异常
EmptyFileException; - 主程序捕获两种异常并打印提示。
🔮 下一篇预告
第 9 篇:《泛型与集合框架:类型安全与灵活性的平衡》
在下一篇中,我们将讨论:
- 为什么 Java 的泛型使用“类型擦除”实现,与 TypeScript 的泛型有何不同;
- 通配符 ? extends 与 ? super 的使用场景与记忆口诀;
- Java Collections Framework(List/Set/Map/Queue)的核心接口与常见实现,以及常见遍历和 Stream 用法。
更多推荐



所有评论(0)