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. 自定义异常:继承异常父类,用于特定业务场景的错误处理
相关推荐
找不到、了5 分钟前
Java排序算法之<插入排序>
java·算法·排序算法
设计师小聂!14 分钟前
力扣热题100----------53最大子数组和
java·数据结构·算法·leetcode
笠码38 分钟前
JVM Java虚拟机
java·开发语言·jvm·垃圾回收
thginWalker44 分钟前
八股文之JVM
java
Cyanto1 小时前
MyBatis-Plus高效开发实战
java·开发语言·数据库
qhd吴飞1 小时前
mybatis 差异更新法
java·前端·mybatis
YuTaoShao2 小时前
【LeetCode 热题 100】51. N 皇后——回溯
java·算法·leetcode·职场和发展
null不是我干的2 小时前
基于黑马教程——微服务架构解析(一)
java·微服务·架构
Bonnie_12152 小时前
04-netty基础-Reactor三种模型
java·nio·jetty
懂得节能嘛.2 小时前
【SpringAI实战】ChatPDF实现RAG知识库
java·后端·spring