Java 异常详解
异常(Exception)是程序运行时发生的不正常情况(错误或意外),Java 通过异常处理机制让程序在出错时不会直接崩溃,而是能优雅地处理错误。
一、异常的核心概念
1. 异常的分类
Java 中所有异常都继承自 Throwable 类,主要分为两大分支:
- Error(错误) :JVM 层面的严重问题(如内存溢出、虚拟机崩溃),程序无法处理,只能避免(如
StackOverflowError、OutOfMemoryError)。 - Exception(异常) :程序本身可以处理的问题,又分为:
- 受检异常(Checked Exception) :编译时必须处理(捕获或声明抛出),如
IOException、SQLException。 - 非受检异常(Unchecked Exception) :运行时才会出现,编译时无需强制处理,如
NullPointerException、ArrayIndexOutOfBoundsException(继承自RuntimeException)。
- 受检异常(Checked Exception) :编译时必须处理(捕获或声明抛出),如
2. 常见异常示例
表格
| 异常类型 | 说明 |
|---|---|
NullPointerException |
调用空对象的方法 / 属性 |
ArrayIndexOutOfBounds |
数组下标越界 |
ClassCastException |
类型转换失败 |
ArithmeticException |
算术错误(如除以 0) |
IOException |
IO 操作失败(如文件读写) |
二、异常的处理方式
Java 处理异常的核心是 try-catch-finally 和 throws/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());
}
}
}
三、自定义异常
当系统内置异常无法满足业务需求时,可自定义异常:
步骤:
- 继承
Exception(受检)或RuntimeException(非受检); - 提供无参构造和带消息的构造方法。
示例:
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());
}
}
}
四、异常处理的最佳实践
- 不要捕获
Throwable(包含 Error,无法处理); - 避免空的
catch块(会隐藏错误); - 优先捕获具体异常(而非直接捕获
Exception); finally中避免抛出异常(会覆盖原异常);- 受检异常用于 "可恢复的错误",非受检异常用于 "编程错误"。
总结
- Java 异常根类是
Throwable,核心分支为Error(不可处理)和Exception(可处理),Exception又分受检(编译时处理)和非受检(运行时处理); - 异常处理的核心方式:
try-catch-finally(捕获处理)、throws(声明抛出)、throw(主动抛出); - 自定义异常需继承
Exception或RuntimeException,用于适配业务场景的错误提示。