Java中的异常

Java 异常详解

异常(Exception)是程序运行时发生的不正常情况(错误或意外),Java 通过异常处理机制让程序在出错时不会直接崩溃,而是能优雅地处理错误。

一、异常的核心概念

1. 异常的分类

Java 中所有异常都继承自 Throwable 类,主要分为两大分支:

  • Error(错误) :JVM 层面的严重问题(如内存溢出、虚拟机崩溃),程序无法处理,只能避免(如 StackOverflowErrorOutOfMemoryError)。
  • Exception(异常) :程序本身可以处理的问题,又分为:
    • 受检异常(Checked Exception) :编译时必须处理(捕获或声明抛出),如 IOExceptionSQLException
    • 非受检异常(Unchecked Exception) :运行时才会出现,编译时无需强制处理,如 NullPointerExceptionArrayIndexOutOfBoundsException(继承自 RuntimeException)。
2. 常见异常示例

表格

异常类型 说明
NullPointerException 调用空对象的方法 / 属性
ArrayIndexOutOfBounds 数组下标越界
ClassCastException 类型转换失败
ArithmeticException 算术错误(如除以 0)
IOException IO 操作失败(如文件读写)

二、异常的处理方式

Java 处理异常的核心是 try-catch-finallythrows/throw

1. 捕获异常(try-catch-finally)

语法

java

运行

复制代码
try {
    // 可能抛出异常的代码
} catch (异常类型1 变量名) {
    // 处理异常类型1
} catch (异常类型2 变量名) {
    // 处理异常类型2(注意:子类异常要写在父类前面)
} finally {
    // 无论是否发生异常,都会执行(常用于释放资源,如关闭流、数据库连接)
}

示例

java

运行

复制代码
public class ExceptionDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        
        try {
            // 可能抛出算术异常的代码
            int result = a / b;
            System.out.println("结果:" + result);
        } catch (ArithmeticException e) {
            // 捕获并处理异常
            System.out.println("错误:除数不能为 0");
            // 打印异常详细信息(调试用)
            e.printStackTrace();
        } finally {
            // 无论是否异常,都会执行
            System.out.println("程序执行完毕");
        }
    }
}

输出

plaintext

复制代码
错误:除数不能为 0
java.lang.ArithmeticException: / by zero
	at ExceptionDemo.main(ExceptionDemo.java:8)
程序执行完毕
2. 声明抛出异常(throws)

如果方法内部不想处理异常,可声明 "抛出",由调用者处理:

java

运行

复制代码
import java.io.FileReader;
import java.io.IOException;

// 声明抛出受检异常 IOException
public class ThrowsDemo {
    public static void readFile() throws IOException {
        // 读取文件可能抛出 IOException
        FileReader fr = new FileReader("test.txt");
        fr.close();
    }

    public static void main(String[] args) {
        try {
            // 调用者必须处理抛出的异常
            readFile();
        } catch (IOException e) {
            System.out.println("文件读取失败:" + e.getMessage());
        }
    }
}
3. 主动抛出异常(throw)

手动触发异常(常用于业务逻辑校验):

java

运行

复制代码
public class ThrowDemo {
    public static void checkAge(int age) {
        if (age < 18) {
            // 主动抛出运行时异常
            throw new IllegalArgumentException("年龄必须≥18");
        }
        System.out.println("年龄合法");
    }

    public static void main(String[] args) {
        try {
            checkAge(16);
        } catch (IllegalArgumentException e) {
            System.out.println("错误:" + e.getMessage());
        }
    }
}

三、自定义异常

当系统内置异常无法满足业务需求时,可自定义异常:

步骤:
  1. 继承 Exception(受检)或 RuntimeException(非受检);
  2. 提供无参构造和带消息的构造方法。

示例

java

运行

复制代码
// 自定义受检异常
public class AgeException extends Exception {
    // 无参构造
    public AgeException() {
        super();
    }

    // 带异常信息的构造
    public AgeException(String message) {
        super(message);
    }
}

// 使用自定义异常
public class CustomExceptionDemo {
    public static void checkAge(int age) throws AgeException {
        if (age < 0 || age > 150) {
            throw new AgeException("年龄必须在 0-150 之间");
        }
        System.out.println("年龄有效");
    }

    public static void main(String[] args) {
        try {
            checkAge(200);
        } catch (AgeException e) {
            System.out.println("异常:" + e.getMessage());
        }
    }
}

四、异常处理的最佳实践

  1. 不要捕获 Throwable(包含 Error,无法处理);
  2. 避免空的 catch 块(会隐藏错误);
  3. 优先捕获具体异常(而非直接捕获 Exception);
  4. finally 中避免抛出异常(会覆盖原异常);
  5. 受检异常用于 "可恢复的错误",非受检异常用于 "编程错误"。

总结

  1. Java 异常根类是 Throwable,核心分支为 Error(不可处理)和 Exception(可处理),Exception 又分受检(编译时处理)和非受检(运行时处理);
  2. 异常处理的核心方式:try-catch-finally(捕获处理)、throws(声明抛出)、throw(主动抛出);
  3. 自定义异常需继承 ExceptionRuntimeException,用于适配业务场景的错误提示。
相关推荐
程序喵大人25 分钟前
【C++进阶】STL算法与函数对象 - 02 sort为什么需要随机访问迭代器
开发语言·c++·算法
SomeB1oody35 分钟前
【RustyML入门】2.6. 线性判别分析
开发语言·后端·机器学习·rust·教程
程序员雷欧1 小时前
环形缓冲区深度解析:从基础原理到Disruptor源码的全面剖析
java
xiaoqiMikko1 小时前
Dependabot 面板全绿,不代表你的 Tomcat 没洞
java·spring boot
前端开发张小七1 小时前
Java 学习笔记 · 第三课:多线程与并发编程(线程、同步、死锁、Lock、乐观锁与悲观锁)
java·后端·程序员
用户昵称1001 小时前
C/C++编程-工程实践-本地存储log的工程意义
c语言·开发语言
花生了什么事o2 小时前
JVM 垃圾回收:对象如何被判定和回收
java·jvm
evans在进步2 小时前
HashMap 为什么线程不安全?ConcurrentHashMap 如何解决?
java·spring boot·spring
我命由我123452 小时前
匈牙利命名法
java·服务器·后端·学习·java-ee·kotlin·学习方法
闲猫2 小时前
LangChain / Integrations / Integrations by component / Tool
java·数据库·langchain