Java异常体系知识梳理

发布时间:2026/7/12 23:26:58

Java异常体系知识梳理 Java异常体系知识梳理作者没有四次元口袋的蓝胖日期2026-07-10标签Java, 异常, Throwable一、异常体系结构Java 异常体系以Throwable为根类分为两大分支Error和Exception。Throwable ├── Error严重错误程序无法处理 │ ├── OutOfMemoryErrorOOM │ ├── StackOverflowError栈溢出 │ ├── NoClassDefFoundError │ └── LinkageError │ └── Exception异常程序可以处理 ├── RuntimeException运行时异常非受检异常 │ ├── NullPointerExceptionNPE │ ├── ArrayIndexOutOfBoundsException │ ├── ClassCastException │ ├── ArithmeticException │ ├── IllegalArgumentException │ ├── IllegalStateException │ └── UnsupportedOperationException │ └── 其他异常受检异常编译时异常 ├── IOException ├── SQLException ├── ClassNotFoundException └── FileNotFoundException1.1 Error vs Exception对比ErrorException严重程度严重错误程序无法恢复一般异常程序可以处理来源JVM 系统级错误程序逻辑错误、外部资源问题处理无法处理只能避免必须处理受检或建议处理示例OOM、StackOverflowNPE、IOException1.2 受检异常 vs 非受检异常对比受检异常Checked非受检异常Unchecked编译检查编译器强制要求处理编译器不强制继承Exception非 RuntimeExceptionRuntimeException处理方式try-catch 或 throws可以不处理但建议处理典型场景文件不存在、SQL错误NPE、数组越界代码示例// 受检异常必须处理publicvoidreadFile()throwsIOException{// 方式1throws 声明FileReaderreadernewFileReader(a.txt);}publicvoidreadFile2(){try{// 方式2try-catchFileReaderreadernewFileReader(a.txt);}catch(IOExceptione){e.printStackTrace();}}// 非受检异常可以不处理但不建议publicvoiddivide(inta,intb){intresulta/b;// ArithmeticException编译不报错}二、异常处理机制2.1 五个关键字关键字作用try包裹可能抛出异常的代码catch捕获并处理异常finally无论是否异常都会执行的代码清理资源throw手动抛出异常对象throws声明方法可能抛出的异常2.2 异常捕获规则规则 1先小后大try{// 可能抛出多种异常}catch(FileNotFoundExceptione){// 子类异常先捕获// 处理文件未找到}catch(IOExceptione){// 父类异常后捕获// 处理其他IO异常}catch(Exceptione){// 最后兜底// 处理所有其他异常}⚠️ 如果先捕获父类异常子类异常的 catch 块永远不会执行编译器会报错。规则 2finally 一定会执行try{returntry;// 即使 try 中有 return}finally{System.out.println(finally 也会执行);}唯一例外调用System.exit(0)或 JVM 崩溃。规则 3finally 中的 return 会覆盖 try/catch 的 returnpublicstaticinttest(){try{return1;}finally{return2;// 最终返回 2}}⚠️ 这是常见面试题finally 的 return 会覆盖前面的 return。2.3 try-with-resourcesJava 7传统写法FileReaderreadernull;try{readernewFileReader(a.txt);// 使用 reader}catch(IOExceptione){e.printStackTrace();}finally{if(reader!null){try{reader.close();// 手动关闭资源}catch(IOExceptione){e.printStackTrace();}}}try-with-resourcestry(FileReaderreadernewFileReader(a.txt)){// 使用 reader}catch(IOExceptione){e.printStackTrace();}// 自动关闭不用 finally 只要实现了AutoCloseable接口的类都可以用 try-with-resources。三、自定义异常3.1 为什么需要自定义异常表达业务语义如UserNotFoundException携带业务错误码统一异常处理3.2 如何自定义步骤继承Exception受检异常或RuntimeException非受检异常提供构造方法无参、带 message、带 cause可选添加业务字段如错误码代码示例// 自定义受检异常publicclassBusinessExceptionextendsException{privateintcode;publicBusinessException(){super();}publicBusinessException(Stringmessage){super(message);}publicBusinessException(intcode,Stringmessage){super(message);this.codecode;}publicintgetCode(){returncode;}}// 自定义非受检异常publicclassUserNotFoundExceptionextendsRuntimeException{publicUserNotFoundException(Stringmessage){super(message);}}// 使用publicvoidfindUser(LonguserId){UseruseruserRepository.findById(userId);if(usernull){thrownewUserNotFoundException(用户不存在userId);}}3.3 选择受检还是非受检场景选择调用方必须处理如 IO 操作继承 Exception受检业务逻辑异常如参数校验失败继承 RuntimeException非受检 Spring 框架中大部分业务异常都继承 RuntimeException因为受检异常会导致代码臃肿。四、常见异常类4.1 NullPointerExceptionNPE产生原因调用 null 对象的方法或属性。Stringstrnull;str.length();// NPE常见场景返回值为 null集合元素为 nullSpring 注入失败Autowired 没匹配到拆箱时包装类为 null避免方法// 1. 提前判空if(str!null){str.length();}// 2. 使用 OptionalOptionalStringoptOptional.ofNullable(str);intlenopt.map(String::length).orElse(0);// 3. 使用工具类Objects.requireNonNull(str,str 不能为 null);4.2 ClassCastException产生原因类型转换错误。Objectobjhello;Integernum(Integer)obj;// ClassCastException避免方法if(objinstanceofInteger){Integernum(Integer)obj;}// Java 16 模式匹配if(objinstanceofIntegernum){// 直接使用 num}4.3 IndexOutOfBoundsException产生原因数组或集合索引越界。int[]arrnewint[3];arr[5]10;// ArrayIndexOutOfBoundsExceptionListStringlistnewArrayList();list.get(0);// IndexOutOfBoundsException避免方法if(index0indexarr.length){arr[index]10;}4.4 ConcurrentModificationException产生原因在迭代集合时修改了集合结构。ListStringlistnewArrayList(Arrays.asList(a,b,c));// 错误边遍历边修改for(Strings:list){if(b.equals(s)){list.remove(s);// 抛出 ConcurrentModificationException}}正确方式// 1. 使用 IteratorIteratorStringitlist.iterator();while(it.hasNext()){if(b.equals(it.next())){it.remove();// 使用 Iterator 的 remove}}// 2. 使用 removeIfJava 8list.removeIf(s-b.equals(s));// 3. 使用 CopyOnWriteArrayList并发场景ListStringsafeListnewCopyOnWriteArrayList();4.5 StackOverflowError产生原因栈溢出通常是无限递归。publicvoidrecursive(){recursive();// 无限递归 → StackOverflowError}常见场景递归没有终止条件递归深度过大方法调用链过长五、异常处理最佳实践5.1 不要吞掉异常// 错误吞掉异常try{doSomething();}catch(Exceptione){// 什么都不做异常被吞掉}// 正确至少记录日志try{doSomething();}catch(Exceptione){log.error(执行失败,e);thrownewBusinessException(操作失败,e);}5.2 不要用异常控制流程// 错误用异常控制逻辑try{returnlist.get(index);}catch(IndexOutOfBoundsExceptione){returnnull;}// 正确先判断if(index0indexlist.size()){returnlist.get(index);}returnnull; 异常是异常情况不是正常流程。异常创建和抛出有性能开销。5.3 捕获具体异常而非 Exception// 不推荐捕获所有异常try{// ...}catch(Exceptione){// 不知道具体是什么异常}// 推荐捕获具体异常try{// ...}catch(IOException|SQLExceptione){// 明确知道可能抛出什么异常}5.4 finally 中不要 return// 错误finally 中 returnpublicinttest(){try{return1;}finally{return2;// 会覆盖 try 的 return}}// 正确finally 只做清理publicinttest(){try{return1;}finally{close();// 只做资源清理}}5.5 及时关闭资源// 推荐try-with-resourcestry(ConnectionconndataSource.getConnection()){// 使用连接}catch(SQLExceptione){log.error(数据库错误,e);}六、全局异常处理Spring在 Web 应用中通常使用ControllerAdvice统一处理异常。RestControllerAdvicepublicclassGlobalExceptionHandler{// 处理自定义业务异常ExceptionHandler(BusinessException.class)publicResulthandleBusinessException(BusinessExceptione){log.error(业务异常{},e.getMessage());returnResult.error(e.getCode(),e.getMessage());}// 处理参数校验异常ExceptionHandler(MethodArgumentNotValidException.class)publicResulthandleValidationException(MethodArgumentNotValidExceptione){Stringmessagee.getBindingResult().getFieldError().getDefaultMessage();returnResult.error(400,message);}// 处理所有其他异常ExceptionHandler(Exception.class)publicResulthandleException(Exceptione){log.error(系统异常,e);returnResult.error(500,系统繁忙);}}七、思维导图速览┌───────────────────────────────────────────────────┐ │ Java 异常体系 │ ├───────────────────────────────────────────────────┤ │ │ │ 体系结构 │ │ Throwable │ │ ├── ErrorJVM 错误无法处理 │ │ └── Exception │ │ ├── RuntimeException非受检 │ │ │ ├── NPE / ClassCast / AIOOB │ │ │ └── 业务异常建议继承 │ │ └── 其他受检必须处理 │ │ ├── IOException / SQLException │ │ └── ClassNotFoundException │ │ │ │ 异常处理 │ │ • try-catch-finally │ │ • try-with-resources自动关闭 │ │ • 先小后大子类异常先捕获 │ │ • finally 一定会执行除 System.exit │ │ │ │ 自定义异常 │ │ • 继承 Exception 或 RuntimeException │ │ • 提供构造方法 │ │ • 可携带业务错误码 │ │ │ │ 常见异常 │ │ • NPE判空 / Optional / requireNonNull │ │ • ClassCastExceptioninstanceof 判断 │ │ • ConcurrentModificationException用 Iterator │ │ • StackOverflowError检查递归 │ │ │ │ 最佳实践 │ │ ✗ 不要吞掉异常 │ │ ✗ 不要用异常控制流程 │ │ ✗ finally 不要 return │ │ ✓ 捕获具体异常 │ │ ✓ 及时关闭资源 │ │ ✓ 全局异常处理ControllerAdvice │ └───────────────────────────────────────────────────┘八、写在最后异常体系结构是基础要能画出 Throwable 的完整层次图受检 vs 非受检是高频面试题要能说清楚区别和处理方式常见异常要知道 NPE、ClassCastException、ConcurrentModificationException 的产生原因和避免方法try-with-resources和全局异常处理是实际开发中的最佳实践

相关新闻