一、为什么要学异常?(血的教训预警)
刚学Java那会儿,我在控制台看到这样的报错:
Exception in thread "main" java.lang.NullPointerException
当时整个人都懵了!就像打游戏突然黑屏,完全不知道哪里出问题(现在想想真是菜得真实😂)。后来才知道——不会处理异常的程序员,写出来的代码比定时炸弹还危险!
举个真实案例:
某电商系统在用户支付时,因为没处理NumberFormatException
(数字转换异常),导致凌晨2点整个支付模块崩溃。第二天技术总监在晨会上直接发飙:“连基础异常都不处理,你们代码是纸糊的吗?!”
二、异常分类图鉴(必看干货)
1. Error:系统级暴击
public class SystemCrash {
public static void main(String[] args) {
int[] arr = new int[Integer.MAX_VALUE]; // 直接触发OutOfMemoryError
}
}
这类错误(如OutOfMemoryError
、StackOverflowError
)就像电脑主板烧了——程序员根本处理不了,只能靠JVM或运维人员抢救。
2. Exception:代码界的感冒发烧
(假装这里有张图:Checked Exception和Unchecked Exception的对比表)
▶ Checked Exception(编译时异常)
- 特点:编译器会强制检查,不处理不让编译
- 常见类型:
IOException
(文件找不到啦)SQLException
(数据库抽风啦)ClassNotFoundException
(类失踪啦)
▶ Unchecked Exception(运行时异常)
- 特点:编译器不检查,运行时才会爆炸
- 常见类型:
NullPointerException
(空指针,新手的噩梦!)ArrayIndexOutOfBoundsException
(数组越界)ArithmeticException
(数学老师气哭的除零错误)
三、异常处理三板斧(附实战代码)
第一式:try-catch-finally(经典永流传)
try {
FileInputStream fis = new FileInputStream("不存在的文件.txt");
} catch (FileNotFoundException e) {
System.out.println("文件呢???"); // 输出友好提示
e.printStackTrace(); // 打印堆栈轨迹(调试必备!)
} finally {
System.out.println("无论成败,都要执行的代码!");
// 比如关闭数据库连接(重要!重要!重要!)
}
避坑指南:
- finally块永远会执行(除非JVM崩溃)
- JDK7+推荐用try-with-resources,自动关流美滋滋
第二式:throws(甩锅大法)
public void readFile() throws IOException {
// 把异常抛给上层调用者处理
Files.readAllLines(Paths.get("神秘文件.xxx"));
}
适用场景:
- 当前方法不知道如何处理时
- 需要统一异常处理的框架级代码
第三式:自定义异常(高端玩法)
// 继承RuntimeException创建自定义异常
class MoneyNotEnoughException extends RuntimeException {
public MoneyNotEnoughException(String message) {
super("余额不足警告!" + message);
}
}
// 使用示例
void pay(double money) {
if(balance < money) {
throw new MoneyNotEnoughException("当前余额:" + balance);
}
}
设计原则:
- 业务异常建议继承
RuntimeException
- 异常命名要见名知义,比如
InvalidUserException
四、异常处理四大禁忌(血泪史总结)
❌ 禁忌1:捕获异常后不作为
try {
// 可能出错的代码
} catch (Exception e) {
// 什么都不写!(这是犯罪!)
}
后果:错误被静默吞噬,后期调试想撞墙
❌ 禁忌2:捕获Throwable
try {
// 业务代码
} catch (Throwable t) { // 把Error也捕获了!
// 会导致OOM等严重问题被掩盖
}
❌ 禁忌3:在循环里捕获异常
for (int i=0; i<100; i++) {
try {
// 可能出错的代码
} catch (Exception e) {
// 循环内捕获影响性能
}
}
改进方案:把try-catch移到循环外层
❌ 禁忌4:日志记录不当
catch (SQLException e) {
logger.error("出错啦:" + e); // 错误!丢失堆栈信息
logger.error("数据库异常", e); // 正确写法!
}
五、高频面试题直击(附答案)
Q1:final、finally、finalize的区别?
- final:修饰符(类不可继承/方法不可重写/变量不可修改)
- finally:异常处理必执行代码块
- finalize:Object类中的垃圾回收方法(JDK9已废弃)
Q2:throw和throws的区别?
- throw:在方法内主动抛出异常对象
- throws:在方法声明中标明可能抛出的异常类型
Q3:try-catch-finally的执行顺序?
- 执行try块
- 若有异常进入catch块
- 无论是否异常都执行finally块
六、总结提升路线
- 新手阶段:掌握try-catch-finally基础用法
- 进阶必备:理解异常分类和继承体系
- 高手之路:
- 学会使用
Throwable.getCause()
追踪根本原因 - 掌握
Thread.setDefaultUncaughtExceptionHandler
全局异常处理 - 研究Spring的
@ControllerAdvice
统一异常处理
- 学会使用
最后送大家一句话:优秀的异常处理不是让程序永不报错,而是让错误发生时,能像老司机一样从容处理! 🚗💨
(本文示例代码已通过JDK17测试,建议边看边动手实践)