☕ 第五章 异常
📄 一句话概括

异常章五节串成一条线:看见事故 → 分清责任 → 掌握对策 → 懂得甩锅 → 印自己的事故报告——从"怕报错"到"会安排后事",程序从此炸得体面。

💡 本章先修

4.1 继承(异常家谱靠它读)、4.4 多态(catch 匹配的底层)、3.4 构造方法(自定义异常 super(message) 的依据)。


1. 五节串成一条线

节 主题 一句话收网
5.1 什么是异常 异常 = 运行时的意外;异常对象 = 事故报告(类型/消息/位置),不处理就当场中断
5.2 两派异常 划分标准是"怪谁"不是"谁严重":自己的 bug 改代码(运行时),外部风险写预案(编译时)
5.3 处理语法 一组接(try/catch/finally)一组甩(throw/throws);安检从 println"贴告示"升级为 throw"拉闸"
5.4 抛出异常 throw 后方法非正常中断;异常沿甩锅链条层层上溯,最终接住或甩给 JVM
5.5 自定义异常 继承 + throw 的组合技:印一张说行话的事故报告,catch 精准分诊

暗线:程序对"意外"的态度在升级——5.1 只会看(读报错)→ 5.3 学会接(换备胎)→ 5.4 学会甩(免责声明)→ 5.5 学会造(分诊系统)。这一章之后,你的程序从"一炸就死"变成"炸了也有安排"。

2. 核心概念速查(12 条,考前 3 分钟扫完)

# 概念 一句话
1 异常 运行时意外,本质是 JVM 现场 new 出来的对象
2 异常对象三要素 类型(什么事故)+ 消息(details)+ 位置(堆栈跟踪)
3 Throwable 家谱 Error(管不了,不碰)和 Exception(主角)两支
4 运行时异常 RuntimeException 子孙,unchecked,编译器放行
5 编译时异常 受检 checked,编译器逼你 try-catch 或 throws
6 try-catch try 监控,catch 接住;炸点后 try 剩余代码不跑
7 finally 收尾专用,两条路都跑,return 拦不住(System.exit 除外)
8 catch 规矩 类型要接得住;多 catch 命中即走;子类在前父类殿后
9 throw 方法体内动作,跟异常对象;甩出后方法非正常中断
10 throws 方法签名声明,跟类名可多个;只是免责转嫁
11 甩锅链条 没 catch 的方法自动上溯,main 也不接 → 甩给 JVM → 中断
12 自定义异常 继承 Exception/RuntimeException + super(message) 传消息

3. 代码模板速查(四段,背熟能救命)

模板一:标准接法(try-catch-finally)

try {
    // 风险代码(越小越好,只包会炸的)
} catch (具体异常类型 e) {
    // 接住后的预案:提示 / 换默认值 / 记日志
} finally {
    // 收尾:无论如何都要做的事(关资源等)
}

模板二:安检甩出(throw + throws)

public void setAge(int age) throws AgeOutOfBoundsException {
    if (age < 0 || age > 150) {
        throw new AgeOutOfBoundsException("年龄 " + age + " 不合法");
    }
    this.age = age;
}

模板三:自定义异常类(背下这个骨架)

public class XxxException extends Exception {          // 业务风险选 Exception
    public XxxException() { super(); }
    public XxxException(String message) { super(message); }  // 忘了这行 → getMessage() 为 null
}

模板四:完整闭环(甩 + 接 + 取消息)

try {
    p.setAge(-5);
} catch (AgeOutOfBoundsException e) {
    System.out.println("安检拦截:" + e.getMessage());
}

4. 本章名坑回顾(七个,个个真会撞)

  1. catch 父类在前 → 后面的子类 catch 永远够不着,编译直接报"已捕获"。
  2. try 包太大 → 语法不错但炸了之后 try 剩余全跳过,本来不想连坐的也连坐了。
  3. catch (Exception e) 一把梭 → 什么都能接 = 什么都分不清,信息全被抹平。
  4. return 拦不住 finally → 就算 catch 里 return,finally 也先跑完才走。
  5. throw 后写代码 → unreachable statement,编译报错(方法已经非正常中断了)。
  6. 忘 super(message) → 消息断传,getMessage() 返回 null。
  7. 重写方法 throws 更大 → 只能比父类小或相等——"编译看左"的延伸,子类不能甩出新锅。

5. 异常章收官:一条安检演进线

3.3  println 贴告示    → 只提示不拦人,错误数据照样进对象
5.3  throw Exception  → 拉闸了,但全场都说"普通话",分不清事故类型
5.5  自定义异常        → 每种业务问题一张专属报告,catch 精准分诊 ✅

异常处理是封装的最后一块拼图:setter 里藏安检、安检用异常表达、异常用继承分类——面向对象的三大特性(封装/继承/多态)在异常这一章全部实战了一遍。

6. 与前面章节的连线(复习用)

7. 毕业自检(5 题,合上笔记答)

❓ Q1:运行时异常和编译时异常,编译器态度差在哪?各举两例。

运行时(unchecked)编译器挥手放行:NullPointerException、ArrayIndexOutOfBoundsException;编译时(checked)当场拦下逼写预案:ParseException、ClassNotFoundException。划分标准是"怪谁":使用者的 bug vs 外部环境风险。

❓ Q2:多 catch 的书写顺序有什么规矩?为什么?

子类在前、父类殿后。父类在前时子类 catch 永远执行不到,编译器直接报错"已捕获该异常"。本质是 catch 按家谱匹配,父类型能接住子类型(多态)。

❓ Q3:throw 和 throws 怎么区分?

throw 是方法体里的动作:throw new XxxException("..."),真把异常对象甩出去,方法从此非正常中断;throws 是方法签名上的声明:void f() throws XxxException,告知调用者"我可能甩这种锅,你表态"。

❓ Q4:自定义异常的 getMessage() 返回 null,最可能的原因?

构造方法里漏了 super(message)——消息没上交父类存档,detailMessage 还是空的。补上那行就通。

❓ Q5(毕业题):设计银行取款,写出异常类和甩出处代码,并说明 main 里怎么接。
public class BalanceNotEnoughException extends Exception {
    public BalanceNotEnoughException(String message) { super(message); }
}

public void withdraw(double amount) throws BalanceNotEnoughException {
    if (amount > balance) throw new BalanceNotEnoughException("余额不足:当前 " + balance);
    balance -= amount;
}

main 里 try { acc.withdraw(500); } catch (BalanceNotEnoughException e) { System.out.println(e.getMessage()); }——余额不足是业务风险(外部风险型),所以继承 Exception 走受检待遇。


8. 下一步学什么


相关笔记