Java异常机制初步理解

最近复习Java知识, 异常我平时就刷刷算法题, 接触的不多都快忘完了, 所以就从初学的角度重新理解一下.

Java 异常机制入门学习笔记

一、什么是异常?

  • 异常本质上是一个 "异常类的对象",所有异常都继承自Throwable类
  • 当程序运行中出现特殊情况(如除数为 0、文件不存在等)时,会创建异常对象并抛出(throw)
  • 异常机制是 Java 中处理程序错误的标准化方式,与 if-else 相比有明显优势:

    • 相当于能设置监听器了, 在发生监听的异常事件后会直接抛出相应异常, if-else必须提前预判哪里会出现异常, 定义好监听器后是一劳永逸的, 同类错误发生后会自动抛出了

    • 支持错误自动向上传播,简化多层方法调用中的错误处理

    • 允许将错误交给更适合处理它的上层代码处理

二、异常的抛出与处理流程

1. 异常的手动抛出(throw)

  • 使用throw关键字手动抛出异常对象:
php 复制代码
if (b == 0) {
    throw new RuntimeException("除数不能为0");
}
  • 有手动抛出自然也有自动抛出, 那就是触发监听事件后就会自动抛出相应异常

不管手动抛出还是自动抛出, 抛出的异常都会像 "错误包裹" 一样,从发生地开始自动向上一层栈帧传递

2. 异常的三种处理方式

  1. 不处理
  • 异常会自动向上一层抛,沿着方法调用链逐层传递,直到被处理或传递到程序顶层main方法处

  • 若一直未被处理,最终由 JVM 处理

  • 表现为:程序崩溃,打印错误堆栈信息

  1. 当场捕获处理( try-catch
Java 复制代码
try {
    // 可能抛出异常的代码
    new Test2().test(1,0);
} catch (Exception e) {
    // 异常处理逻辑
    System.out.println("抓到了");
}
  • 在可能抛出异常的代码块外包裹try,用catch捕获并处理
  • 处理后程序可以继续执行,不会崩溃
  1. 主动向上传递( throws
java 复制代码
public void test(int a, int b) throws Exception {
    if (b == 0) {
        throw new Exception("除数不能为0");
    }
}
  • 在方法声明处用throws声明可能抛出的异常
  • 表示 "当前方法不处理,交给上层调用者处理"
  • 传递过程中会退出当前方法栈帧,后面的代码不再执行

实际上哪怕你不向上抛JVM也会主动向上抛, 只是有些异常不抛根本编译不过, 相当于这种异常是Java在提醒你一定要处理, 这时你可以选择当场捕获或者向上抛

三、什么异常必须抛(不用记, 了解即可)

1. 受检异常 vs 非受检异常

  • 非受检异常
    • 继承自RuntimeException及其子类(如NullPointerException)
    • 不需要在方法上用throws声明(编译器不强制)
    • 通常是代码逻辑错误导致的,应通过改进代码避免
  • 受检异常
    • 除了非受检异常之外的其他异常(如IOException、Exception)
    • 必须在方法上用throws声明,否则编译错误
    • 编译器强制要求处理(要么try-catch,要么throws传递)

2. throws关键字的作用

  • 对受检异常:是 "强制声明",不声明会编译错误
  • 对非受检异常:是 "友好提醒",告知调用者可能存在的风险
  • 本质是告诉调用者:"我这个方法可能会抛出这些异常,你需要处理"

四、异常捕获的方法, 用大于等于的引用变量去接异常对象

  • 可以用:

    • 异常类本身的引用捕获(精确匹配)
    • 异常类的父类引用捕获(大的接小的)
  • 匹配顺序:
    • 编译器从上到下顺序执行, 依次匹配catch块
    • 一旦找到匹配的catch块,就执行该块代码,其他catch块被忽略
  • 最佳实践:
csharp 复制代码
try {
    throw new IOException("文件错误");
} catch (IOException e) { // 精确匹配
    System.out.println("处理IO错误");
} catch (Exception e) { // 父类匹配(如果前面没匹配才会执行)
    System.out.println("处理通用错误");
}
  • 具体异常的catch块放在前面
  • 父类异常的catch块放在后面
  • 捕获后可以通过异常对象的引用(如e)访问异常信息:
    • e.getMessage():获取错误信息
    • e.printStackTrace():打印完整错误堆栈
    • 直接打印toString方法应该也可以直接看到错误信息

五、自定义异常(用的不多)

1. 自定义异常的创建

  • 继承Exception(受检异常)或RuntimeException(非受检异常)
  • 通常需要实现几个构造方法,方便传递错误信息
scala 复制代码
// 自定义受检异常
class MyException extends Exception {
    // 无参构造
    public MyException() {
        super();
    }
   
    // 带错误信息的构造
    public MyException(String message) {
        super(message); 
        // 给父类的私有成员变量传递错误信息, 打印的时候直接打印出message
    }
   
    // 带错误码的构造(自定义扩展)
    public MyException(int code) {
        super("错误码:" + code); 
        // 给父类的私有成员变量传递错误信息, 打印的时候直接打印出code
    }
}

2. 自定义异常的使用

  • 在适当的业务场景手动抛出
  • 用try-catch捕获处理或用throws向上传递
csharp 复制代码
public class Test {
    public static void main(String[] args) {
        try {
            new Test().a();
        } catch (MyException e) {
            System.out.println(e); // 打印异常信息
        }
    }
    private void a() throws MyException {
        // 业务逻辑判断,满足条件时抛出自定义异常
        throw new MyException(1001); // 抛出异常对象
    }
}

3. 自定义异常的意义

  • 区分不同业务逻辑错误(如UsernameExistsException、InsufficientBalanceException)
  • 使错误处理更符合业务需求,代码可读性更高

六、关键概念总结

  1. 异常对象:异常本质是对象,包含错误信息
  1. 抛出与捕获:throw抛出异常,try-catch捕获处理
  1. 传递机制:异常可通过throws向上传递,直到被处理或导致程序崩溃
  1. 类型区分:受检异常必须声明处理,非受检异常可选
  1. 多态匹配:异常捕获支持父类引用匹配子类异常对象
  1. 自定义异常:继承异常父类,用于特定业务场景的错误处理
相关推荐
m0_5873830020 分钟前
智慧场馆解决方案实战指南:从系统架构到落地部署全解析
java·spring boot·架构·系统架构
ZC跨境爬虫24 分钟前
LeetCode 13. 罗马数字转整数(多解法详解 + Java Python 实现)
java·python·leetcode
省长29 分钟前
Sa-Token v1.46.0 发布 🚀,来看看有没有令你心动的功能!
java·后端·开源
LayZhangStrive1 小时前
融360 一面
java·面试·后端开发
城管不管1 小时前
重生——第十次面试之开源中国一面挂
java·linux·开发语言·算法·面试·职场和发展·开源
Java小白笔记1 小时前
Windows系统免软件命令激活
java·网络·人工智能·windows·ai·ai编程
爱敲键盘的猴子2 小时前
深入理解 Java HashMap
java·hashmap
霸道流氓气质2 小时前
Java开发者AI编程常用MCP、Skills、Rules全景汇总
java·开发语言·ai编程
天涯醉青楼2 小时前
Spring Boot 接口返回 Bean 时字段名“变形”的深度剖析与解决方案
java
步行cgn2 小时前
MyBatis 高级映射与延迟加载完全指南
java·tomcat·mybatis