hyx0411

导航

Java异常

Java异常

一、前言

在日常 Java 开发中,空指针、数组越界、类型转换异常、IO异常 无处不在。

所以我们需要知道:

• 异常底层是怎么抛出来的?

• throws 和 throw 到底差在哪?

• 什么时候捕获异常?什么时候直接抛出?

• 为什么不要 catch Exception?

• 业务中为什么要自定义异常?

二、什么是异常?

2.1 定义

异常(Exception):程序运行过程中,违背正常逻辑、可预料、可捕获的非正常情况。

区别:

• 错误:程序无法恢复的致命问题(内存溢出、栈溢出)

• 异常:程序可捕获、可处理、可恢复的问题

Java 把非正常情况封装成异常对象,面向对象统一处理。

三、Java 异常整体体系(核心结构图)

Java 异常顶层父类:Throwable

体系树:
Throwable
├─ Error(系统级错误,无需处理)
└─ Exception(程序级异常,可处理)
├─ 编译异常(受检异常 CheckedException)
└─ 运行异常(运行时异常 RuntimeException)

3.1 Error 错误

代表系统层面的错误:

• StackOverflowError 栈溢出

• OutOfMemoryError 内存溢出

特点:

  1. 程序无法处理

  2. 不需要捕获

  3. 代码层面无能为力,只能优化代码

3.2 Exception 异常(重点)

所有我们需要处理的异常都在这里,分为两大类:

1)受检异常(编译异常)

编译期间必须处理,否则代码报错无法运行

常见:

• IOException IO读写异常

• FileNotFoundException 文件不存在

• SQLException 数据库异常

特点:

• 继承自 Exception 但不继承 RuntimeException

• 强制处理:要么 try-catch,要么 throws 抛出

2)非受检异常(运行时异常)

编译不报错,运行才崩溃

常见:

• NullPointerException 空指针

• ArrayIndexOutOfBoundsException 数组越界

• ClassCastException 类型转换异常

• IllegalArgumentException 参数非法

特点:

• 编译器不强制捕获

• 属于代码不严谨导致,可以通过逻辑规避

四、五大异常关键字彻底讲透

4.1 try、catch、finally

标准结构:

try{
    // 可能出错的代码
}catch(异常类型 e){
    // 异常处理逻辑
}finally{
    // 一定会执行(除非JVM退出)
}

执行规则

  1. try 无异常:跳过 catch,执行 finally

  2. try 有异常:立刻跳转到对应 catch,后续 try 代码不执行

  3. finally 无论是否异常都会执行

finally 唯一不执行的情况****

System.exit(0); 

JVM 直接退出,finally 不会执行

4.2 throw:手动抛出异常

作用:主动制造异常对象抛出

if(age < 0){
    throw new IllegalArgumentException("年龄不能为负数");
}

4.3 throws:声明异常

作用:告知调用者:我这个方法可能报错,需要自己处理

public void readFile() throws IOException{
    // IO操作
}

**throw vs throws **

  1. throw:主动抛异常,运行时动作(代码内部)

  2. throws:异常声明,编译时提醒(方法定义上)

  3. throw 是动作,throws 是提醒

五、异常执行流程

正常流程

  1. 代码出现异常

  2. JVM 自动创建对应异常对象

  3. 抛出异常

  4. 寻找就近的 try-catch

  5. 捕获后执行处理逻辑

  6. 执行 finally

  7. 程序继续运行

无捕获流程

异常逐层向上抛 → 最终抛给 JVM → 程序终止、控制台打印堆栈信息

六、常见异常用法与规范

6.1 多 catch 捕获顺序

先小后大

正确:

catch(NullPointerException e)
catch(Exception e)

错误:
父类在前,子类永远无法匹配

6.2 try-catch-finally 中 return 的执行顺序

  1. return 在 finally 之前执行,但返回值暂存

  2. finally 执行完,再真正返回

6.3 为什么不建议直接 catch(Exception)?

  1. 太宽泛,无法精准定位错误

  2. 容易吞掉未知异常,导致 bug 隐藏

  3. 不利于日志排查问题

七、自定义异常(项目必备)

系统自带异常只能描述技术问题,业务问题必须自定义异常

7.1 为什么需要自定义异常?

• 区分「系统异常」和「业务异常」

• 统一项目错误码、错误信息

• 方便全局异常拦截、统一返回前端

7.2 自定义业务异常示例

// 自定义运行时业务异常
public class BusinessException extends RuntimeException{
    private Integer code;
    private String msg;

    public BusinessException(Integer code,String msg){
        super(msg);
        this.code = code;
        this.msg = msg;
    }
}

业务使用:

public void login(String username){
    if(username == null){
        throw new BusinessException(400,"用户名不能为空");
    }
}

八、异常最佳实践(工作常用)

8.1 能预判的异常,优先if判断

能用 if 避免空指针,就不要靠 try-catch 兜底
try-catch 开销更大

8.2 禁止空捕获(空 catch)

// 极其不推荐!吞异常!
try{
}catch(Exception e){

}

8.3 异常必须打印日志

线上排查问题唯一依据
log.error("用户登录失败",e);

8.4 尽量使用运行时异常

自定义异常继承 RuntimeException
不用频繁写 throws,代码更干净

九、全文总结

  1. Java 异常体系核心分为 Error、编译异常、运行异常

  2. 运行异常是代码不严谨导致,可逻辑规避

  3. 五大关键字:try/catch/finally/throw/throws 各司其职

  4. 项目中必须自定义业务异常区分业务错误

  5. 异常处理原则:精准捕获、日志留存、不吞异常、优先预判

异常是 Java 稳定性的核心,搞懂异常体系,不仅能解决面试题,更能写出健壮、线上少崩的代码。

posted on 2026-05-30 21:35  芸歆  阅读(60)  评论(1)    收藏  举报