【JavaSE全面教学】Java异常处理机制Day11(2026年)

【JavaSE全面教学】Java异常处理机制Day11(2026年) 写在前面这是JavaSE系列的第11篇进入进阶特性阶段。异常处理是写出健壮代码的必备技能面试也经常问。很多人写代码只管happy path不考虑异常情况结果上线后各种崩溃。今天把Java的异常体系彻底讲清楚。文章目录一、什么是异常1.1 异常的概念1.2 为什么要处理异常二、Java异常体系2.1 异常类层次结构2.2 Error vs Exception2.3 受检异常 vs 不受检异常三、异常处理的五种方式3.1 try-catch捕获异常3.2 try-catch-finally3.3 try-finally不捕获只释放资源3.4 throws声明异常3.5 throw手动抛出异常四、自定义异常4.1 为什么需要自定义异常4.2 自定义异常的写法4.3 使用自定义异常五、异常处理的最佳实践5.1 try-with-resourcesJava 7推荐5.2 异常处理的规范5.3 异常链六、面试高频考点考点1final、finally、finalize的区别考点2try-catch-finally的执行顺序考点3受检异常vs不受检异常考点4throw和throws的区别参考资料七、总结一、什么是异常1.1 异常的概念异常程序在运行过程中出现的不正常情况。踩坑提醒新手最容易犯的错误就是忽视异常处理。曾经有一个线上Bug让我记忆犹新——用户上传文件时因为磁盘满了导致IOException但代码里没有处理整个服务直接崩溃影响了所有用户。// 常见的异常场景inta10/0;// ArithmeticExceptionint[]arrnewint[3];arr[5]10;// ArrayIndexOutOfBoundsExceptionStringsnull;s.length();// NullPointerExceptionintnumInteger.parseInt(abc);// NumberFormatException异常的本质Java用对象来表示异常每个异常都是一个类的实例。1.2 为什么要处理异常不处理异常的后果 1. 程序直接崩溃用户体验极差 2. 数据可能丢失或损坏 3. 安全漏洞异常信息暴露系统细节 4. 难以定位和修复问题 处理异常的好处 1. 程序不会意外终止 2. 给用户友好的错误提示 3. 便于记录日志和排查问题 4. 保证资源的正确释放二、Java异常体系2.1 异常类层次结构Throwable所有异常的根类 ├── Error错误程序无法处理 │ ├── OutOfMemoryError // 内存溢出 │ ├── StackOverflowError // 栈溢出 │ └── NoClassDefFoundError // 类找不到 │ └── Exception异常程序可以处理 ├── RuntimeException运行时异常也叫不受检异常 │ ├── NullPointerException // 空指针 │ ├── ArrayIndexOutOfBoundsException // 数组越界 │ ├── ArithmeticException // 算术错误 │ ├── ClassCastException // 类型转换错误 │ ├── NumberFormatException // 数字格式错误 │ └── IllegalArgumentException // 非法参数 │ └── 非RuntimeException编译时异常也叫受检异常 ├── IOException // IO异常 │ ├── FileNotFoundException // 文件找不到 │ └── EOFException // 文件结束 ├── SQLException // 数据库异常 └── ClassNotFoundException // 类找不到2.2 Error vs Exception类型说明能否处理ErrorJVM级别的严重错误不能程序直接退出Exception程序级别的异常能try-catch处理// Error不需要也不应该处理// OutOfMemoryError → 增加JVM内存优化代码// StackOverflowError → 检查递归是否正确// Exception必须或应该处理// IOException → try-catch或throws// NullPointerException → 代码中加null检查2.3 受检异常 vs 不受检异常类型说明编译器检查处理方式受检异常非RuntimeException✅ 强制检查必须try-catch或throws不受检异常RuntimeException❌ 不检查可以不处理但建议处理// 受检异常必须处理publicvoidreadFile()throwsIOException{// 必须声明throwsFileInputStreamfisnewFileInputStream(test.txt);// 编译错误}// 不受检异常可以不处理publicvoiddivide(){inta10/0;// 编译通过运行时才报错}三、异常处理的五种方式3.1 try-catch捕获异常try{// 可能出现异常的代码inta10/0;}catch(ArithmeticExceptione){// 捕获特定异常System.out.println(算术异常e.getMessage());}catch(Exceptione){// 捕获其他异常System.out.println(未知异常e.getMessage());}多个catch的顺序try{// ...}catch(NullPointerExceptione){// 先捕获子类异常System.out.println(空指针异常);}catch(RuntimeExceptione){// 再捕获父类异常System.out.println(运行时异常);}catch(Exceptione){// 最后捕获最大的异常System.out.println(异常);}// ❌ 错误先捕获父类子类永远捕获不到// catch (Exception e) { }// catch (NullPointerException e) { } // 编译错误3.2 try-catch-finally经验之谈finally块是资源释放的保险无论try中发生什么除了System.exitfinally都会执行。这是Java保证资源不泄露的机制。try{// 可能出现异常的代码FileInputStreamfisnewFileInputStream(test.txt);// 使用fis...}catch(IOExceptione){// 处理异常System.out.println(IO异常e.getMessage());}finally{// 无论是否异常都会执行// 通常用于关闭资源System.out.println(finally块执行了);}finally的执行时机publicstaticinttest(){try{System.out.println(try);return1;}catch(Exceptione){System.out.println(catch);return2;}finally{System.out.println(finally);// 一定会执行// return 3; // 不建议在finally中return}}test();// 输出try → finally → 返回1finally不执行的情况// 1. 在try或catch中调用了System.exit()try{System.exit(0);// 直接退出JVM}finally{System.out.println(不会执行);// 不会执行}// 2. 程序所在线程死亡// 3. 关闭CPU3.3 try-finally不捕获只释放资源// 没有catch异常会继续向上抛出// 但finally中的资源释放代码一定会执行FileInputStreamfisnull;try{fisnewFileInputStream(test.txt);intdatafis.read();}finally{if(fis!null){try{fis.close();// 关闭资源也可能抛异常}catch(IOExceptione){e.printStackTrace();}}}3.4 throws声明异常踩坑提醒throws不是把异常抛出去就完事了最终一定要在某个地方捕获处理。如果一路throws到main方法那就是把锅甩给了JVM程序会直接崩溃。// 方法声明throws表示这个方法可能抛出异常// 调用者必须处理publicvoidreadFile(Stringpath)throwsIOException{FileInputStreamfisnewFileInputStream(path);fis.close();}// 调用者处理方式1try-catchpublicvoidcaller1(){try{readFile(test.txt);}catch(IOExceptione){e.printStackTrace();}}// 调用者处理方式2继续throwspublicvoidcaller2()throwsIOException{readFile(test.txt);}经验之谈在分层架构中DAO层抛出SQLExceptionService层转换为业务异常Controller层统一处理返回给前端。这种分层处理异常的方式让代码更清晰。3.5 throw手动抛出异常// throw在方法内部手动抛出异常publicvoidsetAge(intage){if(age0||age150){thrownewIllegalArgumentException(年龄不合法age);}this.ageage;}// throws在方法签名上声明publicvoidsetAge(intage)throwsIllegalArgumentException{if(age0||age150){thrownewIllegalArgumentException(年龄不合法age);}this.ageage;}四、自定义异常4.1 为什么需要自定义异常// Java内置的异常太笼统thrownewException(余额不足);// 不够具体// 自定义异常更语义化thrownewInsufficientBalanceException(余额不足当前余额balance);4.2 自定义异常的写法// 自定义运行时异常不受检classInsufficientBalanceExceptionextendsRuntimeException{publicInsufficientBalanceException(Stringmessage){super(message);}publicInsufficientBalanceException(Stringmessage,Throwablecause){super(message,cause);}}// 自定义受检异常classBusinessExceptionextendsException{privateintcode;// 错误码publicBusinessException(intcode,Stringmessage){super(message);this.codecode;}publicintgetCode(){returncode;}}4.3 使用自定义异常classBankAccount{privatedoublebalance;publicvoidwithdraw(doubleamount)throwsBusinessException{if(amount0){thrownewBusinessException(1001,取款金额必须大于0);}if(amountbalance){thrownewBusinessException(1002,余额不足当前余额balance);}balance-amount;}}publicclassTest{publicstaticvoidmain(String[]args){BankAccountaccountnewBankAccount();try{account.withdraw(-100);}catch(BusinessExceptione){System.out.println(错误码e.getCode());System.out.println(错误信息e.getMessage());}}}五、异常处理的最佳实践5.1 try-with-resourcesJava 7推荐为什么推荐try-with-resources在Java 7之前关闭资源需要写一大堆finally代码既繁琐又容易出错。try-with-resources让资源管理变得优雅而且异常处理也更完善——如果try和close都抛异常close的异常会被抑制不会丢失原始异常信息。// 旧写法手动关闭资源FileInputStreamfisnull;try{fisnewFileInputStream(test.txt);intdatafis.read();}catch(IOExceptione){e.printStackTrace();}finally{if(fis!null){try{fis.close();}catch(IOExceptione){e.printStackTrace();}}}// 新写法自动关闭资源推荐try(FileInputStreamfisnewFileInputStream(test.txt)){intdatafis.read();}catch(IOExceptione){e.printStackTrace();}// fis会自动关闭不需要finally// 多个资源try(FileInputStreamfisnewFileInputStream(input.txt);FileOutputStreamfosnewFileOutputStream(output.txt)){intdata;while((datafis.read())!-1){fos.write(data);}}catch(IOExceptione){e.printStackTrace();}// 两个流都会自动关闭踩坑提醒try-with-resources要求资源必须实现AutoCloseable接口。自定义资源时记得实现这个接口。5.2 异常处理的规范经验之谈生产环境绝对不能直接用e.printStackTrace()它会输出到控制台而不是日志文件一旦服务重启异常信息就丢失了。正确的做法是使用日志框架如SLF4J记录。// ✅ 正确做法publicvoidmethod(){try{riskyOperation();}catch(SpecificExceptione){log.error(具体异常,e);// 记录日志thrownewBusinessException(用户友好的提示);// 抛出业务异常}}// ❌ 错误做法publicvoidmethod(){try{riskyOperation();}catch(Exceptione){e.printStackTrace();// 只打印到控制台生产环境不推荐}}// ❌ 最恶劣吞掉异常publicvoidmethod(){try{riskyOperation();}catch(Exceptione){// 什么都不做异常被吞掉了}}踩坑提醒吞掉异常是调试噩梦曾经排查一个Bug花了整整一天最后发现是某个工具类把异常吞掉了导致上层完全不知道发生了什么。5.3 异常链publicvoidreadFile()throwsBusinessException{try{FileInputStreamfisnewFileInputStream(config.txt);}catch(FileNotFoundExceptione){// 保留原始异常信息thrownewBusinessException(5001,配置文件加载失败,e);}}// 获取异常链try{readFile();}catch(BusinessExceptione){System.out.println(业务异常e.getMessage());System.out.println(原始异常e.getCause().getMessage());e.printStackTrace();// 打印完整异常链}六、面试高频考点考点1final、finally、finalize的区别关键字作用final修饰类/方法/变量表示不可变finally异常处理中的块始终执行finalizeObject的方法GC时调用已废弃考点2try-catch-finally的执行顺序publicstaticinttest(){try{return1;}finally{return2;// finally的return会覆盖try的return}}// 返回2但不建议在finally中return考点3受检异常vs不受检异常// 受检异常IOException、SQLException等// 不受检异常RuntimeException及其子类// 编译器只检查受检异常考点4throw和throws的区别关键字位置作用throw方法体内部手动抛出异常对象throws方法签名上声明方法可能抛出的异常参考资料Oracle官方文档 - ExceptionsBaeldung - Java Exceptions七、总结今天我们学习了✅ Java异常体系的层次结构✅ 五种异常处理方式✅ 自定义异常的写法✅ try-with-resources自动关闭资源✅ 异常处理的最佳实践重点记忆Error不需要处理Exception需要处理受检异常必须try-catch或throwsfinally始终执行System.exit除外生产环境不要用e.printStackTrace()推荐用try-with-resources管理资源下一步预告Day12我们将学习集合框架上——ArrayList、LinkedList等List家族。互动话题你在实际开发中遇到过哪些让你印象深刻的异常欢迎在评论区分享如果这篇文章对你有帮助欢迎点赞、收藏这是【JavaSE全面教学】系列的第11篇关注我看完整套教程本文为【JavaSE全面教学】系列第11篇持续更新中…