Error 与 Exception ------ 异常分类与处理策略
适用版本: JDK 8 难度等级: 基础 核心概念: 受检/非受检异常、Error 体系、异常设计原则
一、Java 异常体系全景图
Throwable
│
┌──────────────┴──────────────┐
│ │
Error Exception
│ │
┌───────┼───────┐ ┌──────┴──────┐
│ │ │ │ │
OutOfMemory ... StackOverflow RuntimeException IOException ...
Error Error │
┌──────┼──────┐
│ │ │
NullPointer ... IllegalArgumentException
| 类型 |
含义 |
处理策略 |
| Error |
JVM 层面的严重问题 |
不应捕获,应让程序终止 |
| RuntimeException |
编程错误(非受检) |
修复代码而非捕获 |
| 受检异常 |
可预见的业务异常 |
必须 try-catch 或 throws |
二、Error 类
public class Error extends Throwable {
public Error() { super(); }
public Error(String message) { super(message); }
public Error(String message, Throwable cause) { super(message, cause); }
public Error(Throwable cause) { super(cause); }
}
常见 Error 子类
| Error 类型 |
含义 |
典型场景 |
OutOfMemoryError |
堆内存耗尽 |
内存泄漏、超大对象 |
StackOverflowError |
栈深度超限 |
无限递归 |
NoClassDefFoundError |
运行时找不到类 |
编译后 jar 被移除 |
VirtualMachineError |
JVM 内部错误 |
极少遇到 |
public class ErrorDemo {
public static void main(String[] args) {
// StackOverflowError
try {
recursive(0);
} catch (StackOverflowError e) {
System.out.println("栈溢出!深度过大");
}
// 模拟 OutOfMemoryError
// byte[][] arrays = new byte[Integer.MAX_VALUE][];
}
static void recursive(int depth) {
recursive(depth + 1);
}
}
三、Exception 类
public class Exception extends Throwable {
public Exception() { super(); }
public Exception(String message) { super(message); }
public Exception(String message, Throwable cause) { super(message, cause); }
public Exception(Throwable cause) { super(cause); }
}
3.1 受检异常 vs 非受检异常
// 受检异常:编译器强制处理
public class CheckedExceptionDemo {
public static void readFile() throws java.io.IOException {
throw new java.io.IOException("文件不存在");
}
public static void main(String[] args) {
try {
readFile();
} catch (java.io.IOException e) {
System.err.println("I/O错误: " + e.getMessage());
}
}
}
// 非受检异常:编译器不强制
public class UncheckedExceptionDemo {
public static void divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("除数不能为零");
}
System.out.println(a / b);
}
public static void main(String[] args) {
divide(10, 2); // OK
// divide(10, 0); // 抛 IllegalArgumentException
}
}
四、自定义异常最佳实践
/**
* 业务异常基类------统一异常体系
*/
public class BusinessException extends RuntimeException {
private final String errorCode;
private final transient Object[] args;
public BusinessException(String errorCode, String message, Object... args) {
super(message);
this.errorCode = errorCode;
this.args = args;
}
public String getErrorCode() { return errorCode; }
public Object[] getArgs() { return args; }
public static BusinessException of(String errorCode, Object... args) {
return new BusinessException(errorCode, format(errorCode, args), args);
}
private static String format(String code, Object... args) {
return "[" + code + "] " + java.util.Arrays.toString(args);
}
public static void main(String[] args) {
throw BusinessException.of("USER_001", "userId", 12345);
}
}
五、面试要点
| 问题 |
关键要点 |
| Error 是否应该捕获 |
通常不应捕获,OOM/StackOverflow 无法恢复 |
| RuntimeException 和 Exception 区别 |
RuntimeException 是非受检异常,不强制处理 |
| 自定义异常选哪个父类 |
业务异常继承 RuntimeException,减少代码污染 |