JavaSE入门学习笔记---异常2(异常的处理)
一、为什么需要异常处理?
上一篇我们知道,异常会打断程序流程导致崩溃:比如用户登录时输入了非法格式的手机号,程序直接闪退,体验极差!
异常处理的核心目标是:“捕获意外,优雅处理,让程序继续运行” —— 就像生活中遇到问题:
- 没带钱买水 → 要么回家拿,要么换个便宜的(解决问题)
- 导航没网 → 切换离线地图,或问路人(替代方案)
- 文档损坏 → 尝试修复,或打开备份(兜底方案)
Java 提供了 3 种核心处理方式,对应不同场景,我们逐一拆解~
二、核心处理方式 1:try-catch 捕获异常(“当场解决”)
🌰 生活场景:
去餐厅吃饭,担心 “菜卖完了”(异常),所以提前和服务员说:“如果宫保鸡丁没了,就换鱼香肉丝”—— 主动预判异常,给出解决方案。
📝 语法格式:
try { // 可能发生异常的代码(比如“点宫保鸡丁”) } catch (异常类型1 异常对象) { // 捕获到“异常类型1”时的处理逻辑(比如“换鱼香肉丝”) } catch (异常类型2 异常对象) { // 捕获到“异常类型2”时的处理逻辑(可选,多异常处理) }✨ 代码示例:处理 “除以 0” 异常
public class ExceptionDemo { public static void main(String[] args) { int a = 10; int b = 0; // 这里会触发ArithmeticException try { // 可能出错的代码:除以0 int result = a / b; System.out.println("结果:" + result); // 异常后不会执行 } catch (ArithmeticException e) { // 捕获到算术异常后的处理逻辑 System.out.println("出错了!原因:" + e.getMessage()); // 打印异常信息 System.out.println("已自动修正:将除数改为1,结果:" + (a / 1)); // 兜底方案 } // 异常处理后,程序继续运行 System.out.println("程序正常结束~"); } }运行结果:
出错了!原因:/ by zero 已自动修正:将除数改为1,结果:10 程序正常结束~📌 关键注意:
- try 块里的代码一旦发生异常,会立即跳转到对应的 catch 块,后续代码不再执行。
- 可以有多个 catch 块(捕获多种异常),但异常类型要从 “子类到父类” 排序(比如先捕NullPointerException,再捕Exception)。
- e.getMessage() 能获取异常详情(比如 “/by zero”),方便调试。
三、核心处理方式 2:finally 释放资源(“无论怎样都要做”)
🌰 生活场景:
去图书馆借书,不管借到没借到(是否发生 “没找到书” 的异常),离开前都要 “归还借阅卡”—— 必须执行的收尾操作。
📝 语法格式(搭配 try-catch):
try { // 可能发生异常的代码(比如“找书”) } catch (异常类型 异常对象) { // 异常处理逻辑(比如“换一本书”) } finally { // 无论是否发生异常,都会执行的代码(比如“还借阅卡”) }✨ 代码示例:处理 “文件读取” 异常(释放资源)
import java.io.FileReader; import java.io.IOException; public class FileDemo { public static void main(String[] args) { FileReader reader = null; // 声明文件读取对象 try { // 可能出错的代码:读取不存在的文件 reader = new FileReader("test.txt"); System.out.println("文件读取成功~"); } catch (IOException e) { System.out.println("文件读取失败:" + e.getMessage()); } finally { // 无论是否异常,都要关闭流(释放资源) try { if (reader != null) { // 避免空指针 reader.close(); System.out.println("文件流已关闭"); } } catch (IOException e) { e.printStackTrace(); } } } }📌 核心作用:
处理 “必须执行的收尾操作”,比如关闭文件流、释放数据库连接、关闭网络请求等 —— 即使 try 或 catch 块里有return,finally 也会执行(除非用System.exit(0)强制退出)。
四、核心处理方式 3:throws 声明异常(“交给上级处理”)
🌰 生活场景:
员工遇到 “客户要求降价 10%”(超出权限的异常),无法自己决定,于是上报给经理(上级)处理 —— 自己不处理,抛给调用者。
📝 语法格式:
// 方法声明时添加throws,说明该方法可能抛出的异常 public static 方法返回值 方法名(参数) throws 异常类型1, 异常类型2 { // 可能发生异常的代码(无需try-catch) }✨ 代码示例:声明 “文件读取” 异常
import java.io.FileReader; import java.io.IOException; public class ThrowsDemo { // 方法声明:可能抛出IOException,交给调用者处理 public static void readFile() throws IOException { FileReader reader = new FileReader("test.txt"); // 可能触发IOException reader.close(); } public static void main(String[] args) { try { // 调用带throws的方法,必须try-catch(或继续throws) readFile(); } catch (IOException e) { System.out.println("处理readFile抛出的异常:" + e.getMessage()); } } }📌 关键注意:
- throws 只是 “声明异常可能发生”,不会实际处理异常 —— 最终必须有人用 try-catch 处理(比如 main 方法),否则程序编译报错。
- 编译时异常(checked)必须声明或处理;运行时异常(unchecked)可选(比如空指针异常可以不声明)。
- 子类重写父类方法时,抛出的异常不能比父类更宽泛(比如父类抛IOException,子类不能抛Exception)。
五、3 种处理方式对比表
处理方式 | 核心逻辑 | 适用场景 | 优点 | 缺点 |
try-catch | 当场捕获并处理 | 异常可本地解决(比如修正参数) | 程序不崩溃,体验好 | 代码略繁琐 |
finally | 无论是否异常都执行 | 释放资源(流、连接等) | 保证资源回收 | 不能单独使用,需配合 |
throws | 抛给上级调用者处理 | 异常超出当前方法处理权限(比如跨层) | 代码简洁,职责清晰 | 需上级处理,否则报错 |
六、新手避坑指南
- 不要捕获Exception(所有异常的父类)—— 会掩盖真正的问题(比如把 Error 也捕获了,无法排查系统级错误)。
- 不要空 catch 块(catch (Exception e) {})—— 异常发生后没有任何提示,调试时找不到原因。
- finally 块里不要写return—— 会覆盖 try/catch 里的 return 值,导致逻辑混乱。
- 运行时异常(比如空指针)优先通过 “逻辑判断避免”(比如提前判空if (obj != null)),而不是依赖 try-catch。
七、学习小结
- 异常处理的核心是 “捕获或声明”,避免程序崩溃。
- try-catch:当场解决;finally:释放资源;throws:交给上级 —— 三者配合使用,覆盖大部分场景。
- 编译时异常必须处理(try-catch 或 throws),运行时异常可优化逻辑避免。
- 实际开发中,优先用 try-catch+finally 处理资源相关异常,用 throws 处理跨层异常(比如 Service 层抛异常给 Controller 层)。